Binding to dependency property in slider control style not working - c#

All the default property bindings are working fine however my custom properties are using the fallback values. Specifically ColorThumb and ColorThumbBorder. As their names imply, these 2 properties color the respective parts of the slider's thumb using a brush.
I've had no problems in the past getting my styles to use custom properties for other controls. Is there something unique about the slider control?
Style Xaml
<Style TargetType="{x:Type local:DMSlider}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TickBar x:Name="TopTick" Visibility="{TemplateBinding TickPlacement}" Fill="{TemplateBinding Foreground}" Placement="Top" Height="4" Grid.Row="0"/>
<TickBar x:Name="BottomTick" Visibility="{TemplateBinding TickPlacement}" Fill="{TemplateBinding Foreground}" Placement="Bottom" Height="4" Grid.Row="0"/>
<Border x:Name="TrackBackground" BorderThickness="1" CornerRadius="0" Margin="5,0" VerticalAlignment="Center" Height="4.0" Grid.Row="1"
Background="{Binding Background, RelativeSource={RelativeSource Mode=TemplatedParent}}"
BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource Mode=TemplatedParent}}">
<Canvas Margin="-6,-1">
<Rectangle Visibility="Hidden" x:Name="PART_SelectionRange" Height="4.0" StrokeThickness="1.0"/>
</Canvas>
</Border>
<Track x:Name="PART_Track" Grid.Row="1">
<Track.DecreaseRepeatButton>
<RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{StaticResource DMSliderRepeatButton}"/>
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Command="{x:Static Slider.IncreaseLarge}" Style="{StaticResource DMSliderRepeatButton}"/>
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb x:Name="Thumb">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="gripBorder" BorderThickness="1"
Background="{Binding ColorThumb,
RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, FallbackValue=Red}"
BorderBrush="{Binding ColorThumbBorder,
RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, FallbackValue=Green}" >
<Rectangle x:Name="grip" Height="15" Width="10" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Properties CS
public partial class DMSlider : Slider
{
public static readonly DependencyProperty ColorSliderLeftProperty =
DependencyProperty.Register("ColorSliderLeft", typeof(Brush), typeof(DMSlider));
public static readonly DependencyProperty ColorSliderRightProperty =
DependencyProperty.Register("ColorSliderRight", typeof(Brush), typeof(DMSlider));
public static readonly DependencyProperty ColorSliderThumbProperty =
DependencyProperty.Register("ColorThumb", typeof(Brush), typeof(DMSlider), new FrameworkPropertyMetadata(Brushes.Orange));
public static readonly DependencyProperty ColorSliderThumbHoverProperty =
DependencyProperty.Register("ColorThumbHover", typeof(Brush), typeof(DMSlider));
public static readonly DependencyProperty ColorSliderThumbBorderProperty =
DependencyProperty.Register("ColorThumbBorder", typeof(Brush), typeof(DMSlider));
public Brush ColorSliderLeft
{
get { return (Brush)GetValue(ColorSliderLeftProperty); }
set { SetValue(ColorSliderLeftProperty, value); }
}
public Brush ColorSliderRight
{
get { return (Brush)GetValue(ColorSliderRightProperty); }
set { SetValue(ColorSliderRightProperty, value); }
}
public Brush ColorThumb
{
get { return (Brush)GetValue(ColorSliderThumbProperty); }
set { SetValue(ColorSliderThumbProperty, value); }
}
public Brush ColorThumbHover
{
get { return (Brush)GetValue(ColorSliderThumbHoverProperty); }
set { SetValue(ColorSliderThumbHoverProperty, value); }
}
public Brush ColorThumbBorder
{
get { return (Brush)GetValue(ColorSliderThumbProperty); }
set { SetValue(ColorSliderThumbBorderProperty, value); }
}
}

Thanks to #Clemens for showing me what I was doing wrong. I needed to reference the slider not the the thumb.
I should have been targeting an ancestor like so:
Background="{Binding ColorThumb, RelativeSource={RelativeSource AncestorType=local:DMSlider}, Mode=TwoWay, FallbackValue=Red}"
BorderBrush="{Binding ColorThumbBorder, RelativeSource={RelativeSource AncestorType=local:DMSlider}, Mode=TwoWay, FallbackValue=Green}"
Full thumb style
<Thumb x:Name="Thumb">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="gripBorder" BorderThickness="1"
Background="{Binding ColorThumb,
RelativeSource={RelativeSource AncestorType=local:DMSlider}, Mode=TwoWay, FallbackValue=Red}"
BorderBrush="{Binding ColorThumbBorder,
RelativeSource={RelativeSource AncestorType=local:DMSlider}, Mode=TwoWay, FallbackValue=Green}">
<Rectangle x:Name="grip" Height="15" Width="10" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>

Related

UpDown control that disables up or down button when limit is reached

My question is based on one of the answer to this post:
Where is the WPF Numeric UpDown control?
answered by Mr. Squirrel.Downy.
What i would like to accomplish is a numeric updown control that increases/decreases with a larger amount when the buttons are pressed for a longer time, otherwise the increase/decrease is the normal amount. Also when the max/min is reached the buttons should disable.
I have a style based on a Slider which contains 2 buttons of type HoldButton (up/down, derived from RepeatButton) and a readonly TextBlock for the Value.
In the HoldButton i have 2 dependency properties for an ICommand. These are ClickAndHoldCommand and ClickCommand which are executed from either the OnPreviewMouseLeftButtonDown() or OnPreviewMouseLeftButtonUp() depending on the length of the mouse button press. In the xaml these are bound to Slider.IncreaseLarge and Slider.IncreaseSmall respectively.
How can i disable the up button when the max is reached and disable the down button when the min is reached? The difficulty is that when i for example disable the slider, the up mouse event does not work anymore...
<Style TargetType="{x:Type Slider}" x:Key="NumericUpDown">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton Grid.Row="2" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="+">
</usercontrols:HoldButton>
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton Grid.Row="4" Delay="250" Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60" Height="60" Width="60" Style="{StaticResource ButtonStyleGeneral}" Content="-">
</usercontrols:HoldButton>
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public partial class HoldButton : RepeatButton
{
private bool buttonIsHeldPressed;
public HoldButton()
{
InitializeComponent();
buttonIsHeldPressed = false;
this.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
// RepeatButton fires click event repeatedly while button is being pressed!
this.Click += HoldButton_Click;
}
private void HoldButton_Click(object sender, RoutedEventArgs e)
{
Trace.WriteLine("HoldButton_Click()");
if (EnableClickHold)
{
if (numberButtonRepeats > 2)
{
ClickAndHoldCommand.Execute(this.CommandParameter);
e.Handled = true;
buttonIsHeldPressed = true;
}
numberButtonRepeats++;
}
}
public bool EnableClickHold
{
get { return (bool)GetValue(EnableClickHoldProperty); }
set { SetValue(EnableClickHoldProperty, value); }
}
public ICommand ClickAndHoldCommand
{
get { return (ICommand)GetValue(ClickAndHoldCommandProperty); }
set { SetValue(ClickAndHoldCommandProperty, value); }
}
public ICommand ClickCommand
{
get { return (ICommand)GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ClickAndHoldCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickAndHoldCommandProperty =
DependencyProperty.Register("ClickAndHoldCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for ClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.Register("ClickCommand", typeof(ICommand), typeof(HoldButton), new UIPropertyMetadata(null));
// Using a DependencyProperty as the backing store for EnableClickHold. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableClickHoldProperty =
DependencyProperty.Register("EnableClickHold", typeof(bool), typeof(HoldButton), new PropertyMetadata(false));
// Using a DependencyProperty as the backing store for MillisecondsToWait. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MillisecondsToWaitProperty =
DependencyProperty.Register("MillisecondsToWait", typeof(int), typeof(HoldButton), new PropertyMetadata(0));
public int MillisecondsToWait
{
get { return (int)GetValue(MillisecondsToWaitProperty); }
set { SetValue(MillisecondsToWaitProperty, value); }
}
private int numberButtonRepeats;
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (EnableClickHold)
{
numberButtonRepeats = 0;
if(!buttonIsHeldPressed)
{
ClickCommand?.Execute(this.CommandParameter);
}
buttonIsHeldPressed = false;
}
}
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("OnPreviewMouseLeftButtonDown()");
if (EnableClickHold)
{
// When numberButtonRepeats comes above 1 then the button is considered to be pressed long
if (numberButtonRepeats > 1)
{
ClickAndHoldCommand?.Execute(this.CommandParameter);
}
numberButtonRepeats++;
}
}
}
<UserControl x:Class="Views.TemperatureControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Views"
xmlns:cal="http://www.caliburnproject.org"
xmlns:controls="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="250" d:DesignWidth="150">
<Slider Minimum="{Binding MinimumTemperature}"
Maximum="{Binding MaximumTemperature}"
SmallChange="{Binding TemperatureTinySteps}"
LargeChange="{Binding TemperatureSmallSteps}"
Value="{Binding ControlValue}"
Style="{StaticResource NumericUpDown}" />
</UserControl>
You should extend the Slider control and implement the logic there.
Finally name the RepeatButton elements and move the Style to the Generic.xaml file.
public class CustomSlider : Slider
{
static CustomSlider()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomSlider), new FrameworkPropertyMetadata(typeof(CustomSlider)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_IncreaseButton = GetTemplateChild(nameof(this.PART_IncreaseButton)) as UIElement;
this.PART_DecreaseButton = GetTemplateChild(nameof(this.PART_DecreaseButton)) as UIElement;
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
if (this.PART_IncreaseButton == null
|| this.PART_DecreaseButton == null)
{
return;
}
this.PART_IncreaseButton.IsEnabled = newValue < this.Maximum;
this.PART_DecreaseButton.IsEnabled = newValue > this.Minimum;
}
private UIElement PART_IncreaseButton { get; set; }
private UIElement PART_DecreaseButton { get; set; }
}
Generic.xaml
Name the HoldButton elements "PART_IncreaseButton" and "PART_IncreaseButton" so that you can find them easily in the template.
<Style TargetType="{x:Type CustomSlider}">
<Style.Resources>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="20" />
</Style>
</Style.Resources>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="SmallChange" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Grid>
<Rectangle RadiusX="10" RadiusY="10" Stroke="{StaticResource SolidBrushLightGrey}" Fill="Black" StrokeThickness="1" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ControlName" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<TextBlock Grid.Row="1" x:Name="ControlUnits" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" />
<usercontrols:HoldButton x:Name="PART_IncreaseButton"
Grid.Row="2"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.IncreaseLarge}"
ClickCommand="{x:Static Slider.IncreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="+" />
<TextBlock Grid.Row="3" x:Name="Temperature" Style="{StaticResource LabelStyle}" Margin="0,5,0,0" FontSize="30" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Value, StringFormat=N1}" />
<usercontrols:HoldButton x:Name="PART_DecreaseButton"
Grid.Row="4"
Delay="250"
Interval="375"
EnableClickHold="True"
ClickAndHoldCommand="{x:Static Slider.DecreaseLarge}"
ClickCommand="{x:Static Slider.DecreaseSmall}"
MaxWidth="60"
Height="60" Width="60"
Style="{StaticResource ButtonStyleGeneral}"
Content="-" />
<Border x:Name="TrackBackground" Visibility="Collapsed">
<Rectangle x:Name="PART_SelectionRange" Visibility="Collapsed" />
</Border>
<Thumb x:Name="Thumb" Visibility="Collapsed" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

virtualization for custom treeview

I made a custom TreeView with multiple columns. Everything worked well until thee are lots of items in the tree.
I tried to enable Virtualization by doing VirtualizingPanel.IsVirtualizing="True" (Will be VirtualizingStackPanel.IsVirtualizing if you are < .NET 4.5) but not only it does not speed it up, it actually made the load time even worse.
On a normal TreeView this property does the trick but I can't find a way to get it to work on my custom Tree
TreeViewItem.cs
public class TreeListViewItem : TreeViewItem
{
protected override DependencyObject GetContainerForItemOverride()
{
return new TreeListViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is TreeListViewItem;
}
}
TreeListView.cs
public class TreeListView : TreeView
{
public GridViewColumnCollection Columns { get; set; }
public TreeListView()
{
Columns = new GridViewColumnCollection();
}
protected override DependencyObject GetContainerForItemOverride()
{
return new TreeListViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is TreeListViewItem;
}
}
Node.cs
public class Node
{
public string Data { get; set; }
public List<Node> Children { get; set; }
public Node(string data)
{
Data = data;
Children = new List<Node>();
}
}
ViewModel.cs
public class ViewModel
{
public ObservableCollection<Node> Nodes { get; private set; }
public ViewModel()
{
Nodes = new ObservableCollection<Node>();
Node parent = new Node("Parent");
for (int i = 0; i < 5000; i++)
parent.Children.Add(new Node(i.ToString()));
Nodes.Add(parent);
}
}
MainWindow.xaml
<Window x:Class="WPFTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFTest"
Title="Test"
mc:Ignorable="d"
Width="200">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="local:TreeListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TreeListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer VerticalScrollBarVisibility="Disabled">
<DockPanel>
<GridViewHeaderRowPresenter Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Top"/>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
DockPanel.Dock="Bottom">
<ItemsPresenter/>
</ScrollViewer>
</DockPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:TreeListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TreeListViewItem">
<StackPanel>
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<GridViewRowPresenter x:Name="PART_Header"
Content="{TemplateBinding Header}"
Columns="{Binding Path=Columns, RelativeSource={RelativeSource AncestorType={x:Type local:TreeListView}}}" >
</GridViewRowPresenter>
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:TreeListView ItemsSource="{Binding Nodes}" VirtualizingPanel.IsVirtualizing="True">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}"/>
</TreeView.ItemTemplate>
<local:TreeListView.Columns>
<GridViewColumn Header="Test" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Data}"/>
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</local:TreeListView.Columns>
</local:TreeListView>
</Grid>
</Window>
I tried templating the ItemPanel to be VirtualizingStackPanel and it did not help either.
I strip out the expander part since it is not relevant. You can double click on the parent node to expand the tree and it will take a long time to load the children.
On the TreeListView style, on the ItemsPresenter's parent ScrollViewer, set CanContentScroll="True":
<ScrollViewer CanContentScroll="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
DockPanel.Dock="Bottom">
<ItemsPresenter/>
</ScrollViewer>
On the TreeListViewItem style, you need to have something named "Expander" (for some reason unknown to me - perhaps some style/code is looking for it?).
Just put a ToggleButton in the style's StackPanel:
<StackPanel>
<ToggleButton x:Name="Expander" Width="0" />
<Border Name="Bd" ... />
....
</StackPanel>
Here is the complete XAML:
<Window x:Class="WpfApplication88.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication88"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="local:TreeListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TreeListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer VerticalScrollBarVisibility="Disabled">
<DockPanel>
<GridViewHeaderRowPresenter Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Top"/>
<ScrollViewer CanContentScroll="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
DockPanel.Dock="Bottom">
<ItemsPresenter/>
</ScrollViewer>
</DockPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:TreeListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TreeListViewItem">
<StackPanel>
<ToggleButton x:Name="Expander" Width="0" />
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<GridViewRowPresenter x:Name="PART_Header"
Content="{TemplateBinding Header}"
Columns="{Binding Path=Columns, RelativeSource={RelativeSource AncestorType={x:Type local:TreeListView}}}" >
</GridViewRowPresenter>
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:TreeListView ItemsSource="{Binding Nodes}" VirtualizingPanel.IsVirtualizing="True">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}"/>
</TreeView.ItemTemplate>
<local:TreeListView.Columns>
<GridViewColumn Header="Test" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Data}"/>
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Test 2" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Data2}"/>
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</local:TreeListView.Columns>
</local:TreeListView>
</Grid>
</Window>
I added a Test2 DataViewColum and added a Data2 property to the Node class to make sure it worked (and it does). Here are the changes to the code:
public class ViewModel
{
public ObservableCollection<Node> Nodes { get; private set; }
public ViewModel()
{
Nodes = new ObservableCollection<Node>();
Node parent = new Node("Parent", "Parent2");
for (int i = 0; i < 5000; i++)
parent.Children.Add(new Node(i.ToString(), (i * i).ToString()));
Nodes.Add(parent);
}
}
public class Node
{
public string Data { get; set; }
public string Data2 { get; set; }
public List<Node> Children { get; set; }
public Node(string data, string data2)
{
Data = data;
Data2 = data2;
Children = new List<Node>();
}
}
FYI - The template that VS made for me (while I was figuring this out), put the CanContentScroll="True" into a setter on a trigger for VirtualizingPanel.IsVirtualizing when it is True. Something like this:
<Style TargetType="local:TreeListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TreeListView">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<GridViewHeaderRowPresenter Columns="{Binding Path=Columns, RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Top"/>
<ScrollViewer x:Name="_tv_scrollviewer_" HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
DockPanel.Dock="Bottom">
<ItemsPresenter/>
</ScrollViewer>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="VirtualizingPanel.IsVirtualizing" Value="True">
<Setter Property="CanContentScroll" TargetName="_tv_scrollviewer_" Value="True"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Dependency> Property was already registered by <type>

I know this question was already asked by many peoples. I got answer for that also. but somehow the DP which I had created is not setting properly in my styles.
Here is my code. [Listing-1]
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type local:DataGridColumnFilter}">
<Style.Resources>
<local:BooleanToHeightConverter x:Key="booleanToHeightConverter" />
<local:FontSizeToHeightConverter x:Key="fontSizeToHeightConverter" />
<local:MyOppositeBooleanToVisibilityConverter x:Key="oppositeBooleanToVisibilityConverter" />
<local:ClearFilterButtonVisibilityConverter x:Key="clearFilterButtonVisibilityConverter" />
<Style TargetType="{x:Type local:DelayTextBox}">
<Setter Property="Background" Value="AliceBlue" />
</Style>
</Style.Resources>
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Self}, Path=IsControlInitialized, Converter={StaticResource booleanToHeightConverter}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DataGridColumnFilter}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Name="PART_FilterContainer"
Visibility="{Binding Path=AssignedDataGridColumn.DoNotGenerateFilterControl, RelativeSource={RelativeSource AncestorType={x:Type local:DataGridColumnFilter}}, Converter={StaticResource oppositeBooleanToVisibilityConverter}}"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Clear Filter Button -->
<Button
VerticalAlignment="Top"
Height="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Converter={StaticResource fontSizeToHeightConverter},
Path=FontSize}"
ToolTip="Clear filter"
Command="{Binding Path=(local:DataGridExtensions.ClearFilterCommand), RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
Grid.Column="0" Name="PART_ClearFilterButton">
<Button.Content>
Clear filter
</Button.Content>
<Button.Visibility>
<MultiBinding Converter="{StaticResource clearFilterButtonVisibilityConverter}">
<Binding Path="IsFirstFilterControl" RelativeSource="{RelativeSource TemplatedParent}"/>
<Binding Path="(local:DataGridExtensions.IsClearButtonVisible)" RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}"/>
</MultiBinding>
</Button.Visibility>
</Button>
<!-- Clear Filter Button -->
<!-- Numeric and Text-->
<local:DelayTextBox
Visibility="Collapsed"
VerticalAlignment="Top"
VerticalContentAlignment="Center"
Height="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Converter={StaticResource fontSizeToHeightConverter},
Path=FontSize}"
Text="{Binding
RelativeSource={RelativeSource AncestorType={x:Type local:DataGridColumnFilter}},
Path=FilterCurrentData.QueryString,
Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
Grid.Column="2" Name="PART_TextBoxFilter">
</local:DelayTextBox>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsTextFilterControl" Value="true">
<Setter TargetName="PART_TextBoxFilter" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Resources>
<local:BooleanToHeightConverter x:Key="booleanToHeightConverter" />
<local:FontSizeToHeightConverter x:Key="fontSizeToHeightConverter" />
<local:MyOppositeBooleanToVisibilityConverter x:Key="oppositeBooleanToVisibilityConverter" />
<local:ClearFilterButtonVisibilityConverter x:Key="clearFilterButtonVisibilityConverter" />
<!--<Style TargetType="{x:Type local:DelayTextBox}">
<Setter Property="Background" Value="AliceBlue" />
</Style>-->
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<local:DataGridColumnFilter Margin="1" Grid.Column="0" Grid.Row="0"
FilterCurrentData ="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGridColumnHeader}},
Path=Tag,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"
AssignedDataGridColumnHeader ="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGridColumnHeader}},
Path=.}"
AssignedDataGridColumn ="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGridColumnHeader}},
Path=Column}"
DataGrid ="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=.}"
DataGridItemsSource ="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=ItemsSource}"
>
</local:DataGridColumnFilter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding ListOfStudents,Mode=TwoWay}"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" local:DataGridColumnExtensions.DoNotGenerateFilterControl="True"
/>
<DataGridTextColumn Header="Address" Binding="{Binding Path=Address}" local:DataGridColumnExtensions.DoNotGenerateFilterControl="true"
/>
<DataGridTextColumn Header="Tel" Binding="{Binding Path=Tel}"
/>
</DataGrid.Columns>
</DataGrid>
</Grid>
I'm getting error when I initialize property as follows :- [Listing-2]
public class DataGridColumnExtensions
{
public static DependencyProperty IsCaseSensitiveSearchProperty =
DependencyProperty.RegisterAttached("IsCaseSensitiveSearch",
typeof(bool), typeof(DataGridColumn));
public static bool GetIsCaseSensitiveSearch(DependencyObject target)
{
return (bool)target.GetValue(IsCaseSensitiveSearchProperty);
}
public static void SetIsCaseSensitiveSearch(DependencyObject target, bool value)
{
target.SetValue(IsCaseSensitiveSearchProperty, value);
}
public static DependencyProperty IsBetweenFilterControlProperty =
DependencyProperty.RegisterAttached("IsBetweenFilterControl",
typeof(bool), typeof(DataGridColumn));
public static bool GetIsBetweenFilterControl(DependencyObject target)
{
return (bool)target.GetValue(IsBetweenFilterControlProperty);
}
public static void SetIsBetweenFilterControl(DependencyObject target, bool value)
{
target.SetValue(IsBetweenFilterControlProperty, value);
}
public static DependencyProperty DoNotGenerateFilterControlProperty =
DependencyProperty.RegisterAttached("DoNotGenerateFilterControl",
typeof(bool), typeof(DataGridColumn), new PropertyMetadata(false));
public static bool GetDoNotGenerateFilterControl(DependencyObject target)
{
return (bool)target.GetValue(DoNotGenerateFilterControlProperty);
}
public static void SetDoNotGenerateFilterControl(DependencyObject target, bool value)
{
target.SetValue(DoNotGenerateFilterControlProperty, value);
}
}
}
After searching on StackOverflow I found that I should declare property as follows [Listing-3]
public class DataGridColumnExtensions
{
public static DependencyProperty IsCaseSensitiveSearchProperty =
DependencyProperty.RegisterAttached("IsCaseSensitiveSearch",
typeof(bool), typeof(DataGridColumnExtensions));
public static bool GetIsCaseSensitiveSearch(DependencyObject target)
{
return (bool)target.GetValue(IsCaseSensitiveSearchProperty);
}
public static void SetIsCaseSensitiveSearch(DependencyObject target, bool value)
{
target.SetValue(IsCaseSensitiveSearchProperty, value);
}
public static DependencyProperty IsBetweenFilterControlProperty =
DependencyProperty.RegisterAttached("IsBetweenFilterControl",
typeof(bool), typeof(DataGridColumnExtensions));
public static bool GetIsBetweenFilterControl(DependencyObject target)
{
return (bool)target.GetValue(IsBetweenFilterControlProperty);
}
public static void SetIsBetweenFilterControl(DependencyObject target, bool value)
{
target.SetValue(IsBetweenFilterControlProperty, value);
}
public static DependencyProperty DoNotGenerateFilterControlProperty =
DependencyProperty.RegisterAttached("DoNotGenerateFilterControl",
typeof(bool), typeof(DataGridColumnExtensions), new PropertyMetadata(false));
public static bool GetDoNotGenerateFilterControl(DependencyObject target)
{
return (bool)target.GetValue(DoNotGenerateFilterControlProperty);
}
public static void SetDoNotGenerateFilterControl(DependencyObject target, bool value)
{
target.SetValue(DoNotGenerateFilterControlProperty, value);
}
}
Then error from XAML disappears but I cannot get value of property DoNotGenerateFilterControl in my style where I'm setting visibility of Grid named as "PART_FilterContainer" (In Style ).
Also value of property was applied to my grid when I use Incorrect Dependency property code [Listing-2] but then XAML throws an error.
This is my first time to post question on StackOverflow. So please forgive as I had posted whole code.
Since DoNotGenerateFilterControl is an attached property, you should reference it as such when binding to it. Attached properties should be enclosed in parentheses, so instead of binding to
AssignedDataGridColumn.DoNotGenerateFilterControl
You should bind to
AssignedDataGridColumn.(local:DataGridColumnExtensions.DoNotGenerateFilterControl)
<Grid Name="PART_FilterContainer"
Visibility="{Binding Path=AssignedDataGridColumn.(local:DataGridColumnExtensions.DoNotGenerateFilterControl), RelativeSource={RelativeSource AncestorType={x:Type local:DataGridColumnFilter}}, Converter={StaticResource oppositeBooleanToVisibilityConverter}}">
This might be a workaround:
Maybe instead you could use a DataTrigger in XAML to control the visibility of the grid. Something like this:
<Grid Name="PART_FilterContainer">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DoNotGenerateFilterControl, ElementName=WhatEverElement}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

Change circle button background image on click in WPF

I have a circle button below
<Button x:Name="btnLight" Width="72" Height="72" Content="" Margin="180,0,372,94" VerticalAlignment="Bottom" d:LayoutOverrides="VerticalAlignment">
<Button.Template>
<ControlTemplate>
<Grid>
<Ellipse>
<Ellipse.Fill>
<ImageBrush ImageSource="Images/light-off.jpg"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
How do I change the background image (Images/light-on.jpg) when I click it?
Thank you!
Wow! You have been given some complicated answers here... you're all doing too much work!!! This question has a really simple solution. First, let's sort out this ControlTemplate the way it should be:
<Button x:Name="btnLight" Width="72" Height="72" Content="" Margin="180,0,372,94"
VerticalAlignment="Bottom">
<Button.Template>
<ControlTemplate>
<Ellipse Name="Ellipse" Fill="{TemplateBinding Background}" />
</ControlTemplate>
</Button.Template>
</Button>
Now, you can add a really simple Style to perform your image change:
<Style TargetType="{x:Type Button}">
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="Images/Add_16.png" />
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="Images/Copy_16.png" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
To do it properly you need to create a view model that contains a handler for the button to call when it's pressed and a boolean property you can use for a datatrigger to change the image. Start with the view model:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public RelayCommand OnClickedCommand { get; private set; }
private bool _ImageChanged;
public bool ImageChanged
{
get { return this._ImageChanged; }
private set {
this._ImageChanged = value;
OnPropertyChanged("ImageChanged");
}
}
public ViewModel()
{
this.OnClickedCommand = new RelayCommand(param => OnClicked());
}
private void OnClicked()
{
this.ImageChanged = true;
}
}
Now create an instance of it and set it as your button's data context. Your button XAML should looks something like this:
<Button x:Name="btnLight" Margin="148,0,372,63" VerticalAlignment="Bottom" Command="{Binding OnClickedCommand}" Height="69">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse>
<Ellipse.Fill>
<ImageBrush ImageSource="image1.png"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ImageChanged}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse>
<Ellipse.Fill>
<ImageBrush ImageSource="image2.png"/>
</Ellipse.Fill>
</Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Bind DependencyProperty in style

I'm sure this is a simple thing to do, and to look up, but I've yet to have any luck. Basically I want to style a WPF Slider such that when used I can give it two additional strings to display.
The new control looks like this:
public class SliderPicker : Slider
{
public string LeftLabel
{
get { return (string)GetValue(LeftLabelProperty); }
set { SetValue(LeftLabelProperty, value); }
}
public static readonly DependencyProperty LeftLabelProperty =
DependencyProperty.Register("LeftLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
public string RightLabel
{
get { return (string)GetValue(RightLabelProperty); }
set { SetValue(RightLabelProperty, value); }
}
public static readonly DependencyProperty RightLabelProperty =
DependencyProperty.Register("RightLabel", typeof(string), typeof(SliderPicker), new UIPropertyMetadata(String.Empty));
}
The style like this:
<Style x:Key="SlickSlider" TargetType="{x:Type Slider}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And usage:
<controls:SliderPicker LeftLabel="RealPreference" RightLabel="RealCoverage" Width="400" Style="{StaticResource SlickSlider}"/>
This doesn't seem to work. So, how do I set the DP on the control and show it in the template? I thought that's what TemplateBinding was for?
You only need a small fix. Change {x:Type Slider} to {x:Type controls:SliderPicker}
You need to apply the custom control as the type in your style and template. Without it your trying to look for LeftLabelProperty and RightLabelProperty in Slider and not SliderPicker with your template binding.
Change your style to
<Style x:Key="SlickSlider" TargetType="{x:Type controls:SliderPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:SliderPicker}">
...
<TextBlock Text="{TemplateBinding LeftLabel}" Grid.Row="2" HorizontalAlignment="Left" />
<TextBlock Text="{TemplateBinding RightLabel}" Grid.Row="2" HorizontalAlignment="Right" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Have tried this with the class you've posted and it works fine :)

Categories

Resources