Вопрос или проблема
Я работаю над созданием приложения с очень специфическим пользовательским интерфейсом. В моем приложении есть TreeView, который имеет полосу прокрутки. К сожалению, полоса прокрутки выглядит очень просто и выглядит как обычная полоса прокрутки Windows. Мне нужно, чтобы она больше соответствовала текстурам пользовательского интерфейса, которые я использую в своем приложении, но у меня не получается правильно отредактировать Thumb, чтобы он принимал .png.
Скрипт .xaml
<Window x:Class="TestAPP.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestAPP"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- Регистрируем ScrollThumbConverter -->
<local:ScrollThumbConverter x:Key="ScrollThumbConverter"/>
<local:ThumbHeightConverter x:Key="ThumbHeightConverter" />
<!-- Пользовательский стиль ScrollBar -->
<Style x:Key="CustomScrollBar" TargetType="{x:Type ScrollBar}">
<Setter Property="Width" Value="15"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid Background="Transparent" Margin="0,0,0,116">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Верхняя стрелка -->
<RepeatButton Grid.Row="0" Height="15" Width="15"
Click="UpArrow_Click"
BorderThickness="0">
<RepeatButton.Background>
<ImageBrush ImageSource="/154965.png" Stretch="Fill"/>
</RepeatButton.Background>
</RepeatButton>
<!-- Полоса прокрутки с MultiBinding -->
<Track Grid.Row="1" Name="PART_Track">
<Track.Thumb>
<Thumb Height="50" Width="15" BorderThickness="0" Padding="0">
<Thumb.Template>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid>
<Image Source="/154971.png" Stretch="Fill"/>
</Grid>
</ControlTemplate>
</Thumb.Template>
<!-- MultiBinding для положения thumb -->
<Thumb.RenderTransform>
<MultiBinding Converter="{StaticResource ScrollThumbConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=ScrollViewer}" Path="VerticalOffset" />
<Binding RelativeSource="{RelativeSource AncestorType=ScrollViewer}" Path="ScrollableHeight" />
<Binding RelativeSource="{RelativeSource AncestorType=ScrollViewer}" Path="ViewportHeight" />
</MultiBinding>
</Thumb.RenderTransform>
</Thumb>
</Track.Thumb>
</Track>
<!-- Нижняя стрелка -->
<RepeatButton Grid.Row="2" Height="15" Width="15"
Click="DownArrow_Click"
BorderThickness="0">
<RepeatButton.Background>
<ImageBrush ImageSource="/154964.png" Stretch="Fill"/>
</RepeatButton.Background>
</RepeatButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!-- ScrollViewer с пользовательской полосой прокрутки -->
<ScrollViewer x:Name="MyScrollViewer"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Disabled"
ScrollChanged="ScrollViewer_ScrollChanged">
<ScrollViewer.Style>
<Style TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ScrollContentPresenter Grid.Column="0"/>
<ScrollBar Grid.Column="1" Style="{StaticResource CustomScrollBar}" Orientation="Vertical"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ScrollViewer.Style>
<!-- Пример содержимого для прокручивания -->
<StackPanel>
<TextBlock Text="Строка 1" Height="50"/>
<TextBlock Text="Строка 2" Height="50"/>
<TextBlock Text="Строка 3" Height="50"/>
<TextBlock Text="Строка 4" Height="50"/>
<TextBlock Text="Строка 5" Height="50"/>
<TextBlock Text="Строка 6" Height="50"/>
<TextBlock Text="Строка 7" Height="50"/>
<TextBlock Text="Строка 8" Height="50"/>
<TextBlock Text="Строка 9" Height="50"/>
<TextBlock Text="Строка 10" Height="50"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
Скрипт .xaml.cs
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace TestAPP
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Прокрутка вверх при нажатии на стрелку вверх
private void UpArrow_Click(object sender, RoutedEventArgs e)
{
MyScrollViewer.LineUp(); // Прокрутка на одну строку вверх
}
// Прокрутка вниз при нажатии на стрелку вниз
private void DownArrow_Click(object sender, RoutedEventArgs e)
{
MyScrollViewer.LineDown(); // Прокрутка на одну строку вниз
}
// Обработка изменений прокрутки (вы можете вручную настраивать размер thumbs или обрабатывать любую визуальную логику здесь)
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
// По желанию: добавьте любую дополнительную логику для управления движением thumbs или другими событиями прокрутки.
}
}
public class ScrollThumbConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 3 || !(values[0] is double verticalOffset) || !(values[1] is double scrollableHeight) || !(values[2] is double viewportHeight))
{
return new TranslateTransform(0, 0); // Предоставляемое преобразование по умолчанию
}
// Рассчитываем положение thumb
double trackHeight = scrollableHeight + viewportHeight; // Общая высота трека
double ratio = scrollableHeight > 0 ? verticalOffset / scrollableHeight : 0;
double thumbPosition = ratio * (trackHeight - viewportHeight); // Рассчитываем положение thumb
return new TranslateTransform(0, thumbPosition);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ThumbHeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2 || !(values[0] is double viewportHeight) || !(values[1] is double scrollableHeight))
{
return 50.0; // Высота thumb по умолчанию, если значения отсутствуют
}
// Рассчитываем высоту thumb как соотношение между областью просмотра и общей высотой прокрутки
double totalHeight = scrollableHeight + viewportHeight;
double thumbHeight = (viewportHeight / totalHeight) * viewportHeight;
return Math.Max(20, thumbHeight); // Обеспечиваем минимальную высоту thumb для удобства использования
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Я работаю с .xaml (WPF) и C#. Ниже я добавил тестовую версию скрипта, с которым я работаю. Стрелки работают и выглядят именно так, как мне нужно, но я не могу правильно отобразить thumb. Что я делаю не так?
Ответ или решение
Для изменения внешнего вида "ползунка" (Thumb) в WPF с использованием пользовательского изображения (.png) необходимо убедиться, что изображение загружается правильно и отображается в элементе. В вашем случае вы уже определили стиль для полосы прокрутки, и добавили ControlTemplate
для Thumb
, в котором используется Image
. Давайте посмотрим, что можно исправить и настроить, чтобы всё работало как нужно.
Шаги для исправления
-
Проверьте путь к изображению: Убедитесь, что ваше изображение действительно доступно по указанному пути. Если ваш файл
154971.png
находится в папке проекта, правильно укажите путь:<Image Source="pack://application:,,,/YourAssemblyName;component/Images/154971.png" Stretch="Fill"/>
Замените
YourAssemblyName
на название вашей сборки (обычно имя проекта), а также укажите путь к папке, где находится изображение. -
Ваша разметка для Thumb: Убедимся, что вы правильно используете
ControlTemplate
дляThumb
. В вашем коде выглядит корректно, но проверьте, что элементы интерфейса не скрыты. Добавьте отладочные отступы, чтобы убедиться, чтоThumb
виден:<Thumb Height="50" Width="15" BorderThickness="0" Padding="0" Background="Transparent"> <Thumb.Template> <ControlTemplate TargetType="{x:Type Thumb}"> <Grid Background="Transparent"> <Image Source="pack://application:,,,/YourAssemblyName;component/Images/154971.png" Stretch="Fill"/> </Grid> </ControlTemplate> </Thumb.Template> </Thumb>
-
Изменение размера ползунка: Возможно, вам потребуется настроить размер ползунка в зависимости от размера содержимого. В классе
ThumbHeightConverter
, который вы уже определили, убедитесь, что вы возвращаете разумное значение для высотыThumb
. Корректное вычисление высоты ползунка важно для правильного отображения:public class ThumbHeightConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 2 || !(values[0] is double viewportHeight) || !(values[1] is double scrollableHeight)) { return 50.0; // Значение по умолчанию, если значения отсутствуют } double totalHeight = viewportHeight + scrollableHeight; double thumbHeight = (viewportHeight / totalHeight) * viewportHeight; return Math.Max(20, thumbHeight); // Минимальная высота ползунка } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
-
Обработка события прокрутки: У вас есть обработчик для
ScrollChanged
, который может использоваться для изменения высоты ползунка или его позиции. Так как вы его оставили пустым, убедитесь, что он необходим или добавьте логику для приспособления поведения ползунка.
Пример исправленной разметки
Вот как может выглядеть часть вашего кода с учетом вышеописанных изменений:
<Track Grid.Row="1" Name="PART_Track">
<Track.Thumb>
<Thumb Height="{Binding Path=ActualHeight, ElementName=MyScrollViewer}"
Width="15"
BorderThickness="0"
Padding="0">
<Thumb.Template>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid Background="Transparent">
<Image Source="pack://application:,,,/YourAssemblyName;component/Images/154971.png" Stretch="Fill"/>
</Grid>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
Заключение
Следуя данным рекомендациям и проверив пути к ресурсам, вы сможете заставить ваш ползунок отображаться с нужным пользовательским изображением. Убедитесь, что файлы изображений корректно добавлены в проект и доступны для использования в WPF. Если проблемы сохраняются, возможные дополнительные сниппеты кода или отладочная информация также могут помочь в диагностике.