I have a sets of user controls in my WPF application. One of them I've listed in code below:
RichSlider.xaml - control directly
<UserControl x:Name="RichSliderControl"
x:Class="CustomControls.RichSlider"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Controls;component/Themes/RichSlider.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
</UserControl>
RichSlider.xaml - control style
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CustomControls="clr-namespace:CustomControls"
>
<Style x:Key="GlobalLabelStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="0"/>
<Setter Property="TextElement.FontSize" Value="14"/>
<Setter Property="TextElement.FontWeight" Value="Bold"/>
<Setter Property="TextElement.FontFamily" Value="Century Gothic"/>
</Style>
<Style x:Key="ValueLabelStyle" TargetType="{x:Type Label}" BasedOn="{StaticResource GlobalLabelStyle}">
<Setter Property="Content" Value="{Binding Value, ElementName=RichSliderControl, Mode=OneWay}"/>
</Style>
<Style TargetType="{x:Type Slider}">
<Setter Property="Margin" Value="0"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="Background" Value="#F5F5F5"/>
<Setter Property="IsSnapToTickEnabled" Value="True"/>
<Setter Property="Minimum" Value="{Binding Minimum, ElementName=RichSliderControl}"/>
<Setter Property="Maximum" Value="{Binding Maximum, ElementName=RichSliderControl}"/>
<Setter Property="Value" Value="{Binding Value, ElementName=RichSliderControl}"/>
<Setter Property="SmallChange" Value="{Binding SmallChange, ElementName=RichSliderControl}"/>
<Setter Property="LargeChange" Value="{Binding LargeChange, ElementName=RichSliderControl}"/>
</Style>
<ControlTemplate x:Key="HorizontalSlider" TargetType="{x:Type UserControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalContentAlignment="Center" Content="{Binding Text, ElementName=RichSliderControl, Mode=OneWay}"></Label>
<Label Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalContentAlignment="Center" Style="{StaticResource ValueLabelStyle}"></Label>
<Slider x:Name="ValueSlider"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
TickPlacement="BottomRight"
>
</Slider>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="VerticalSlider" TargetType="{x:Type UserControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center">
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" Text="{Binding Text, ElementName=RichSliderControl, Mode=OneWay}"></TextBlock>
</Label>
<Label Grid.Row="1" Grid.Column="1" HorizontalContentAlignment="Center" Style="{StaticResource ValueLabelStyle}"></Label>
<Slider x:Name="ValueSlider"
Grid.Row="0"
Grid.Column="1"
Orientation="Vertical"
TickPlacement="TopLeft"
IsDirectionReversed="True"
>
</Slider>
</Grid>
</ControlTemplate>
<Style TargetType="{x:Type CustomControls:RichSlider}">
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="MinWidth" Value="100" />
<Setter Property="MinHeight" Value="20" />
<Setter Property="Template" Value="{StaticResource HorizontalSlider}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="MinWidth" Value="20" />
<Setter Property="MinHeight" Value="100" />
<Setter Property="Template" Value="{StaticResource VerticalSlider}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
RichSlider.xaml.cs - control code
public partial class RichSlider : UserControl
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(RichSlider));
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(RichSlider));
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(RichSlider));
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(RichSlider));
public static readonly DependencyProperty SmallChangeProperty =
DependencyProperty.Register("SmallChange", typeof(double), typeof(RichSlider));
public static readonly DependencyProperty LargeChangeProperty =
DependencyProperty.Register("LargeChange", typeof(double), typeof(RichSlider));
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(RichSlider));
private static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<double>), typeof(RichSlider));
[Bindable(true)]
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
[Bindable(true)]
public double Value
{
get => (double)GetValue(ValueProperty);
set
{
var val = value;
if (val < Minimum)
{
val = Minimum;
}
if (val > Maximum)
{
val = Maximum;
}
SetValue(ValueProperty, val);
RaiseEvent(new(ValueChangedEvent));
}
}
[Bindable(true)]
public double Minimum
{
get => (double)GetValue(MinimumProperty);
set
{
var min = value;
if (Value < min)
{
Value = min;
}
if (Maximum < min)
{
Maximum = min;
}
SetValue(MinimumProperty, min);
}
}
[Bindable(true)]
public double Maximum
{
get => (double)GetValue(MaximumProperty);
set
{
var max = value;
if (Value > max)
{
Value = max;
}
if (max < Minimum)
{
max = Minimum;
}
SetValue(MaximumProperty, max);
}
}
[Bindable(true)]
public double SmallChange
{
get => (double)GetValue(SmallChangeProperty);
set
{
var change = value;
if (change > LargeChange)
{
change = LargeChange;
}
SetValue(SmallChangeProperty, change);
}
}
[Bindable(true)]
public double LargeChange
{
get => (double)GetValue(LargeChangeProperty);
set
{
var change = value;
if (change < SmallChange)
{
change = SmallChange;
}
SetValue(LargeChangeProperty, change);
}
}
[Bindable(true)]
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public RichSlider()
{
InitializeComponent();
}
public event RoutedPropertyChangedEventHandler<double> ValueChanged
{
add
{
AddHandler(ValueChangedEvent, value);
}
remove
{
RemoveHandler(ValueChangedEvent, value);
}
}
public override void OnApplyTemplate()
{
if (GetTemplateChild("ValueSlider") is Slider slider)
{
slider.MouseWheel += new MouseWheelEventHandler(ValueSlider_MouseWheel);
slider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(ValueSlider_ValueChanged);
}
base.OnApplyTemplate();
}
private void ValueSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
RaiseEvent(new(ValueChangedEvent));
}
private void ValueSlider_MouseWheel(object sender, MouseWheelEventArgs e)
{
var directionReversed = (sender as Slider).IsDirectionReversed;
if (((e.Delta > 0 && !directionReversed) || (e.Delta < 0 && directionReversed)) && Value < Maximum)
{
Value++;
}
else if (((e.Delta < 0 && !directionReversed) || (e.Delta > 0 && directionReversed)) && Value > Minimum)
{
Value--;
}
}
}
Ok. I'm adding this control to my window.xaml and binding some value like:
xmlns:CustomControls="clr-namespace:CustomControls;assembly=Controls"
....
<CustomControls:RichSlider Minimum="1" Maximum="5" SmallChange="1" Orientation="Horizontal" Value="{Binding Path=Acceleration, Mode=OneWay}" Text="{DynamicResource Acceleration}"></CustomControls:RichSlider>
And I have a ListBox of items, which contains Name
and Acceleration
properties. So, when I'm clicking on ListBoxItem, the value in my RichSlider
is updating according to the value of Acceleration
property in item. So, seems like the binding is working ok.
But no. When I'm changing the value in my RichSlider
manually, the value is no longer updated for all other items and is stuck with the value I set. As evidence:
<TextBox Text="{Binding Path=Name, Mode=OneWay}"></TextBox>
The Text
value is updating after selecting another item, not depends from changing I made. I also checked the way like:
<TextBox Text="{Binding Path=Acceleration, Mode=OneWay}"></TextBox>
and it's also works fine. So, this way I made the conclusion that the issue is in my control, not in Binding, cause for TextBox
it works, but for RichSlider
- not.
So, does anyone have any thoughts what I'm doing wrong?