Unhandled exception `The parameter is incorrect` in NotifyCollectionChangedEvent handler - c#

I have a very simple ListView whose ItemsSource is a ObservableCollection. Better show it with code:
MainPage.xaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Windows.UI.Xaml.Shapes"
x:Class="Test.MainPage" Background="Black" >
<Grid x:Name="Board" Background="Transparent" >
<ListView ItemsSource="{x:Bind LineList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Line">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock Foreground="White" Text="{x:Bind Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Mainpage.xaml.cs:
public sealed partial class MainPage : Page
{
public ObservableCollection<Line> LineList = new ObservableCollection<Line>();
public MainPage()
{
InitializeComponent();
LineList.CollectionChanged += List_CollectionChanged;
LineList.Add(new Line { Name = "Line1" });
LineList.Add(new Line { Name = "Line2" });
}
private void List_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.Action == NotifyCollectionChangedAction.Add)
{
Board.Children.Add(e.NewItems[0] as Line);//if I comment out this line, no exception
}
}
}
What I actually want is that, when I add a Line on the ListView to show it's Name, it be also added in the Grid as an actual Shape. Note that, I am using the ListView only to show the Names of those Lines, and in the Grid I want an actual Line Shape
I don't know what I've done wrong, but the above attempt gives the stated Exception.
If these informations help:
No Exception occurs if I don't add the Line in the Grid
No Exception if : Board.Children.Add(new Line { Name = "Line2" });

I've been fiddling around with your code and I was able to track down what is wrong with your code. However I'm not really sure why it's happening.
The reason why you're getting errors is because you're trying to use same instance of an UIElement (i.e. Line) that you're binding to your ListView.ItemsSource. Why it's failing, is a bit of mystery to me. I suspect that it's forbidden to Bind and add the same UIElement to XAML, as it might create binding loops!? That's just a wild guess though. Anyways...
You shouldn't be using UIElement as the binding context - I can't think of any scenario that you would do such thing. You will be better off by creating a separate model, as per my previous answer (e.g. LineViewModel), and using that as your BindingContext. Your MainPage.xaml.cs code could look like this:
public sealed partial class MainPage : Page
{
public ObservableCollection<LineViewModel> Lines = new ObservableCollection<LineViewModel>();
public MainPage()
{
InitializeComponent();
Lines.CollectionChanged += LinesOnCollectionChanged;
Lines.Add(new LineViewModel { Name = "Line1" });
Lines.Add(new LineViewModel { Name = "Line2" });
}
private void LinesOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
MainGrid.Children.Add(new Line()
{
Name = (e.NewItems[0] as LineViewModel)?.Name ?? string.Empty,
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 12,
X1 = 0,
X2 = 10000
});
}
}
}
public class LineViewModel
{
public string Name { get; set; }
}
The MainPage.xaml will stay the same, as per my previous answer

I'm not sure if this is what you're after but here it goes
MainPage.xaml
<Page x:Class="App4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<Style x:Key="LineViewItemContainerStyle"
TargetType="ListViewItem">
<Setter Property="FontFamily"
Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize"
Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Background"
Value="{ThemeResource ListViewItemBackground}" />
<Setter Property="Foreground"
Value="{ThemeResource ListViewItemForeground}" />
<Setter Property="TabNavigation"
Value="Local" />
<Setter Property="IsHoldingEnabled"
Value="True" />
<Setter Property="Padding"
Value="0" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
<Setter Property="MinWidth"
Value="{ThemeResource ListViewItemMinWidth}" />
<Setter Property="MinHeight"
Value="{ThemeResource ListViewItemMinHeight}" />
<Setter Property="AllowDrop"
Value="False" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="FocusVisualMargin"
Value="0" />
<Setter Property="FocusVisualPrimaryBrush"
Value="{ThemeResource ListViewItemFocusVisualPrimaryBrush}" />
<Setter Property="FocusVisualPrimaryThickness"
Value="2" />
<Setter Property="FocusVisualSecondaryBrush"
Value="{ThemeResource ListViewItemFocusVisualSecondaryBrush}" />
<Setter Property="FocusVisualSecondaryThickness"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter x:Name="Root"
CheckBrush="{ThemeResource ListViewItemCheckBrush}"
ContentMargin="{TemplateBinding Padding}"
CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CheckMode="{ThemeResource ListViewItemCheckMode}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
DragBackground="{ThemeResource ListViewItemDragBackground}"
DragForeground="{ThemeResource ListViewItemDragForeground}"
FocusVisualSecondaryBrush="{TemplateBinding FocusVisualSecondaryBrush}"
FocusVisualPrimaryThickness="{TemplateBinding FocusVisualPrimaryThickness}"
FocusVisualSecondaryThickness="{TemplateBinding FocusVisualSecondaryThickness}"
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
FocusVisualPrimaryBrush="{TemplateBinding FocusVisualPrimaryBrush}"
FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Control.IsTemplateFocusTarget="True"
PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}"
SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverSelected">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverPressed">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PressedSelected">
<VisualState.Setters>
<Setter Target="Root.(RevealBrush.State)"
Value="Pressed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="DisabledStates">
<VisualState x:Name="Enabled" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="Root.RevealBorderThickness"
Value="0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ListViewItemPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="Transparent">
<ListView ItemContainerStyle="{StaticResource LineViewItemContainerStyle}"
ItemsSource="{x:Bind Lines, Mode=OneTime}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:LineViewModel">
<Grid x:Name="ItemGrid">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Foreground="Black"
Text="{x:Bind Name, Mode=OneWay}" />
<Border Grid.Row="1"
BorderBrush="Black"
BorderThickness="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public ObservableCollection<LineViewModel> Lines = new ObservableCollection<LineViewModel>();
public MainPage()
{
InitializeComponent();
Lines.Add(new LineViewModel { Name = "Line1" });
Lines.Add(new LineViewModel { Name = "Line2" });
}
}
public class LineViewModel
{
public string Name { get; set; }
}
Note, that instead of using Line I used Border. Also, I needed to override base ListViewItemContainerStyle to set HorizontalContentAlignment and VerticalContentAlignment to Stretch, so that the DataTemplate elements can take up the entire space of the item
The result:

Related

How to set StyleClass for MenuItem from AppShell programmatically

I want so create my own MenuItems in the AppShell, this is working for me, but now I want to add StyleClass to this MenuItems. I have a StylClass Resource for MenuItems in the AppShell. If I add a MenuItem in AppShell.xaml I can use it without any problem, but my question is, how to that for my on created MenuItems in the code bind.
AppShell.xaml
<Shell.Resources>
<ResourceDictionary>
<Style Class="MenuItemLayoutStyle" TargetType="Layout" ApplyToDerivedTypes="True">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter TargetName="FlyoutItemLabel" Property="Label.TextColor" Value="{StaticResource Primary}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ResourceDictionary>
</Shell.Resources>
AppShell.xaml.cs
private static MenuItem CreateMenuItem(string title, ICommand cmd)
{
var menuItem = new MenuItem
{
Text = title,
Command = cmd,
//Set StyleClass here
};
return menuItem;
}
How can I use the StyleClass from the Sheel.Resources in the code behind?
Thanks for you help
Firstly, define one style for Menuitem, like this:
<Style Class="menuitemstyle" TargetType="Label">
<Setter Property="TextColor" Value="Red" />
<Setter Property="HeightRequest" Value="100" />
</Style>
Then add one MenuItem by code behind in AppShell.cs and use style from Shell.Resources.
public AppShell()
{
InitializeComponent();
Items.Add(new MenuItem() { Text="test 1",StyleClass=new[] { "menuitemstyle" } });
}
Finally, you can get the screenshot:

UWP TreeView sort leads to random events

Team,
I run in a very weird issue with treeview.
Here are the steps to rep:
1. create a new project
2. select Window Studio Template
3. Select MVVM Light
4. Add treeview page
5. create project
6. Open TreeViewPage.xaml and add with:
xmlns:Custom="using:Microsoft.UI.Xaml.Controls"
xmlns:helper="using:TitoDoc2020.Helpers"
<Page.Resources>
<Style x:Key="RadioButtonRevealStyle" TargetType="RadioButton">
<Setter Property="Background" Value="{ThemeResource ToggleButtonRevealBackground}" />
<Setter Property="Foreground" Value="{ThemeResource ToggleButtonForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToggleButtonRevealBorderThemeThickness}" />
<Setter Property="Padding" Value="{ThemeResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid
x:Name="RootGrid"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundPointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushPointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundPressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushPressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundDisabled}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundChecked}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushChecked}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundChecked}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedPointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedPointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedPressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedPressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedPressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="CheckedDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundCheckedDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushCheckedDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundCheckedDisabled}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Indeterminate">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminate}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminate}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminate}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePointerOver">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="PointerOver" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePointerOver}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePointerOver}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminatePressed">
<VisualState.Setters>
<Setter Target="RootGrid.(RevealBrush.State)" Value="Pressed" />
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminatePressed}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminatePressed}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminatePressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="IndeterminateDisabled">
<VisualState.Setters>
<Setter Target="RootGrid.Background" Value="{ThemeResource ToggleButtonRevealBackgroundIndeterminateDisabled}" />
<Setter Target="ContentPresenter.BorderBrush" Value="{ThemeResource ToggleButtonRevealBorderBrushIndeterminateDisabled}" />
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource ToggleButtonForegroundIndeterminateDisabled}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="treeViewColumn" MinWidth="150" MaxWidth="350" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!--641 is the default CompactModeThresholdWidth in NavigationView -->
<AdaptiveTrigger MinWindowWidth="641" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="header.Margin" Value="0,0,0,0" />
<Setter Target="treeViewColumn.Width" Value="350" />
<Setter Target="treeViewColumn.MaxWidth" Value="500" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid
Background="{ThemeResource SystemChromeMediumLowColor}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
Margin="80,0,0,0"
x:Name="header"
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="TreeViewTitle"
Margin="{StaticResource SmallLeftMargin}"
Style="{StaticResource ListTitleStyle}"
VerticalAlignment="Center" />
<Button
Grid.Column="1"
x:Uid="TreeView_CollapseAllButton"
Content=""
FontSize="14"
Padding="{StaticResource SmallLeftRightMargin}"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Command="{Binding ElementName=collapseBehavior, Path=CollapseAllCommand}"
Background="Transparent" />
</Grid>
<StackPanel
Grid.Row="1"
x:DefaultBindMode="OneWay"
Orientation="Horizontal">
<Custom:RadioButtons MaxColumns="2">
<RadioButton
x:Name="SortDate"
Width="50"
Height="50"
MinWidth="50"
Margin="{StaticResource ButtonPadding}"
Background="Transparent"
Click="{x:Bind ViewModel.RadioButton_ClickAsync}"
CornerRadius="2,2,2,2"
Style="{StaticResource RadioButtonRevealStyle}"
Checked="{x:Bind ViewModel.RadioButton_Checked}">
<RadioButton.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="32"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="1"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<StackPanel Grid.Row="1" Grid.Column="0">
<FontIcon
Margin="0,6,0,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource SystemAccentColorDark2}"
Glyph="{x:Bind ViewModel.ImageSrcD}" />
</StackPanel>
</Grid>
</RadioButton.Content>
</RadioButton>
<RadioButton
x:Name="SortAlph"
Width="50"
Height="50"
MinWidth="50"
Margin="{StaticResource ButtonPadding}"
Background="Transparent"
Click="{x:Bind ViewModel.RadioButton_ClickAsync}"
CornerRadius="2,2,2,2"
Style="{StaticResource RadioButtonRevealStyle}"
Checked="{x:Bind ViewModel.RadioButton_Checked}">
<RadioButton.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="32"
Glyph="" />
<FontIcon
Grid.Row="0"
Grid.Column="1"
Height="20"
VerticalAlignment="Top"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<FontIcon
Grid.Row="1"
Grid.Column="1"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Foreground="Transparent"
Glyph="" />
<StackPanel Grid.Row="1" Grid.Column="0">
<FontIcon
Margin="0,6,0,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Foreground="{ThemeResource SystemAccentColorDark2}"
Glyph="{x:Bind ViewModel.ImageSrcA}" />
</StackPanel>
</Grid>
</RadioButton.Content>
</RadioButton>
</Custom:RadioButtons>
</StackPanel>
<winui:TreeView
x:Name="treeView"
Grid.Row="2"
SelectionMode="Single"
ItemsSource="{x:Bind ViewModel.SampleItems}"
ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}">
<i:Interaction.Behaviors>
<behaviors:TreeViewCollapseBehavior x:Name="collapseBehavior" />
<ic:EventTriggerBehavior EventName="ItemInvoked">
<ic:InvokeCommandAction Command="{x:Bind ViewModel.ItemInvokedCommand}" />
</ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
</winui:TreeView>
</Grid>
</Grid>
</Page>
Open TreeViewModel.cs and add replace with:
public string _sorting;
public bool _DateAsc;
public bool _AlphAsc;
public bool _SwapCheck = false;
public bool _filterData = false;
public int _filterDataSpan = 0;
public string ImageSrcD
{
get
{
if (_DateAsc)
{
return "\xF0AD";
}//SvgImageSource svgImage = new SvgImageSource(new Uri("ms-appx:///Assets/Image_16x.png"));
else
{
return "\xF0AE";
}
}
set { Set<bool>(ref _DateAsc, bool.Parse(value)); }
}
public string ImageSrcA
{
get
{
if (_AlphAsc)
{
return "\xF0AD";
}//SvgImageSource svgImage = new SvgImageSource(new Uri("ms-appx:///Assets/Image_16x.png"));
else
{
return "\xF0AE";
}
}
set { Set<bool>(ref _AlphAsc, bool.Parse(value)); }
}
public async Task LoadDataAsync()
{
var data = await SampleDataService.GetTreeViewDataAsync();
foreach (var item in data)
{
SampleItems.Add(item);
}
}
public async Task SortDataAsync()
{
var data = await SampleDataService.GetTreeViewDataAsync();
switch (_sorting)
{
default:
break;
case "SortAlph":
if (_AlphAsc)
{
data = data
.OrderBy(b => b.Country).ThenBy(b => b.CompanyName)
.ToList();
}
else
{
data = data
.OrderByDescending(b => b.Country).ThenByDescending(b => b.CompanyName)
.ToList();
}
break;
}
SelectedItem = null;
SampleItems.Clear();
foreach (var item in data)
{
SampleItems.Add(item);
}
}
public async void RadioButton_Checked(object sender, RoutedEventArgs e)
{
_SwapCheck = true;
/*string sorting = await ApplicationData.Current.LocalSettings.ReadAsync<string>("TreeViewSort");
switch (((RadioButton)sender).Name)
{
case "SortDate":
if (sorting == "SortDate")
{
return;
}
SortAlph.IsChecked = false;
break;
case "SortAlph":
if (sorting == "SortAlph")
{
return;
}
SortDate.IsChecked = false;
break;
default:
break;
}
ApplicationData.Current.LocalSettings.SaveAsync("TreeViewSort", ((ToggleButton)sender).Name);
_sorting = ((RadioButton)sender).Name;
SortTreeAsync();
//await LoadTreeAsync();
*/
}
public void RadioButton_ClickAsync(object sender, RoutedEventArgs e)
{
switch (((RadioButton)sender).Name)
{
case "SortAlph":
if (!_SwapCheck)
{
ImageSrcA = (!_AlphAsc).ToString();
}
break;
case "SortDate":
if (!_SwapCheck)
{
ImageSrcD = (!_DateAsc).ToString();
}
break;
default:
break;
}
_SwapCheck = false;
_sorting = ((RadioButton)sender).Name;
SortDataAsync();
//SortTreeAsync();
}
}
}
under Helpers folder create a class named TitoDoc2020Enums.cs paste:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TitoDoc2020.Helpers
{
public enum LoadingStatus
{
NotStarted = 0,
Loading = 1,
Loaded = 2
}
public enum VisualizationFormat
{
// Surname, Name
SurCName= 0,
// Surname Name
SurnName = 1,
// Name Surname
NameSur = 2
}
public enum Sorting
{
// Surname, Name
Surname = 0,
// Surname Name
Name = 1
}
public enum DateFilter
{
Day = 1, // 59583, // 
Week = 2, // 59584, // 
Month = 3, // 59271, // 
Year = 4, // // 
All = 0 //60041 // &#xEA89
}
}
Update all NuGet libraries and add compile
Click the right sorting icon multiple times, you can open some tree view items, everything happens... items get opened automatically, I even got the request to use the microphone
It does not seem to me I'm doing anything strange... why is so messed up?
nice to see you again, I could run this code sample and reproduce your problem easily. The problem is that you have not specific bool IsExpanded property for the model and bind it to TreeViewItem IsExpanded property. When you reorder the data current UI state will be covered by previous.
Please add the following to your model class and bind IsExpanded in xaml code.
public class SampleCompany : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (isExpanded != value)
{
isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
}
public class SampleOrder : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (isExpanded != value)
{
isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
}
Xaml code
<DataTemplate x:Key="CompanyTemplate" x:DataType="model:SampleCompany">
<winui:TreeViewItem
AutomationProperties.Name="{x:Bind CompanyName}"
IsExpanded="{x:Bind IsExpanded, Mode=OneWay}"
ItemsSource="{x:Bind Orders}">
<TextBlock Margin="{StaticResource XXSmallTopRightBottomMargin}" Text="{x:Bind CompanyName}" />
</winui:TreeViewItem>
</DataTemplate>
<DataTemplate x:Key="OrderTemplate" x:DataType="model:SampleOrder">
<winui:TreeViewItem
AutomationProperties.Name="{x:Bind OrderID}"
IsExpanded="{x:Bind IsExpanded, Mode=OneWay}"
ItemsSource="{x:Bind Details}">
<TextBlock Margin="{StaticResource XXSmallTopRightBottomMargin}" Text="{x:Bind ShortDescription}" />
</winui:TreeViewItem>
</DataTemplate>
Update
I've added the code but the Set part is never used and the Get part is read only when the parent is accessed
It's by-design, because, we use OneWay model, it will not edit the model property when you expand the tree view node. If we set it as TwoWay model set method will called.
BUT now the issue is that when I re-create the object I loose the status so they are all closed again.
When we re-create object but not specific IsExpanded value (default value is false),so all the nodes will close by default.

KeyDown Event is triggered twice

I have a style which includes two TextBoxes in order to create a PlaceHolder / Watermark, so far is working fine.The only exception is that the events are triggered twice, the first one comes from the CustomTextBox I have in the Style, and the second one from the CustomTextBox I have in XAML.Is there any way to prevent this behaviour? I have already tried to set IsEnable="False" and ReadOnly="True" but doesn't seems to work.Here the style I use to simulate the Watermark:
<Style x:Key="CustomTextBoxStyle"
TargetType="{x:Type utils:CustomTextBox}">
<Setter Property="FontFamily"
Value="/UserInterface;component/Resources/Fonts/#Avenir LT Std 35 Light" />
<Setter Property="FontSize"
Value="16" />
<Setter Property="Foreground"
Value="#FF414042" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type utils:CustomTextBox}">
<Border Name="Border"
BorderBrush="#FF348781"
BorderThickness="0,0,0,4"
CornerRadius="2">
<ScrollViewer x:Name="PART_ContentHost"
Margin="0" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="ReadOnly" />
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type utils:CustomTextBox}"
BasedOn="{StaticResource CustomTextBoxStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type utils:CustomTextBox}">
<Grid>
<utils:CustomTextBox
Text="{TemplateBinding Text}"
x:Name="textSource"
Background="Transparent"
Panel.ZIndex="2"
Style="{StaticResource CustomTextBoxStyle}"
KeyboardViewModel="{TemplateBinding KeyboardViewModel}"/>
<utils:CustomTextBox Text="{TemplateBinding HintText}"
Background="{TemplateBinding Background}"
Panel.ZIndex="1"
IsEnabled="False">
<utils:CustomTextBox.Style>
<Style TargetType="{x:Type utils:CustomTextBox}"
BasedOn="{StaticResource CustomTextBoxStyle}">
<Setter Property="Foreground"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference textSource}}"
Value="">
<Setter Property="Foreground"
Value="Gray" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
</DataTrigger>
</Style.Triggers>
</Style>
</utils:CustomTextBox.Style>
</utils:CustomTextBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any help would be appreciated. Thanks in advance.
EDIT 1: Code on the CustomTextBox Class for the Event handling
protected override void OnKeyDown(KeyEventArgs e)
{
//Filtering "solution"
if (e.Source is CustomTextBox sourceTextBox && sourceTextBox.Name.Equals("textSource"))
{
return;
}
base.OnKeyDown(e);
if (e.Key == Key.Enter && EnterKeyCommand != null)
{
if (EnterKeyCommand.CanExecute(null))
{
EnterKeyCommand.Execute(null);
}
}
}
EDIT 2:
Use of the CustomTextBox on my UserControl:
<Utils:CustomTextBox Grid.Row="0"
Margin="0,10"
KeyboardViewModel="{Binding Path=MainWindowViewModel.KeyboardViewModel}"
HintText="Patient"
x:Name="Patient"/>
Based on your comment, first it comes from the "test source" and then goes to CustomTextBox means that the KeyDown event is received on first user control and then passed to the second user control.
Can you make sure that you put e.Handled = true in the "test source" to notify that it is handled on the first user control?
"Fixed" filtering the e.Source as sugesstion from #Amol Bavannavar on the comments:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Source is CustomTextBox sourceTextBox && sourceTextBox.Name.Equals("textSource"))
{
return;
}
base.OnKeyDown(e);
if (e.Key == Key.Enter && EnterKeyCommand != null)
{
if (EnterKeyCommand.CanExecute(null))
{
EnterKeyCommand.Execute(null);
}
}
}

How to create a custom WPF XAML style for check box images

I have a C# WPF Page and on it I have placed several small images that I want to act like check boxes (I have my own custom images for hover and selected states).
I am manually changing the images like so:
<Image x:Name="Image_Custom" Source="/Images/checkcircle_off.png" Width="16" Height="16" HorizontalAlignment="Left" Margin="30,107,0,0" VerticalAlignment="Top" MouseEnter="Image_Custom_MouseEnter" MouseLeave="Image_Custom_MouseLeave" MouseUp="Image_Custom_MouseUp" MouseLeftButtonDown="Image_Custom_MouseLeftButtonDown"/>
private void Image_Custom_MouseEnter(object sender, MouseEventArgs e)
{
if (_selected == false)
{
var uriSource = new Uri("/Images/checkcircle_hover.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
}
}
private void Image_Custom_MouseLeave(object sender, MouseEventArgs e)
{
if (_selected == false)
{
var uriSource = new Uri("/Images/checkcircle_off.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
}
}
private void Image_Custom_MouseUp(object sender, MouseButtonEventArgs e)
{
if (_selected)
{
var uriSource = new Uri("/Images/checkcircle_off.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
_selected = false;
}
else
{
var uriSource = new Uri("/Images/checkcircle_on.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
_selected = true;
}
}
private void Image_Custom_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (_selected)
{
var uriSource = new Uri("/Images/checkcircle_off.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
_selected = false;
}
else
{
var uriSource = new Uri("/Images/checkcircle_on.png", UriKind.Relative);
Image_Custom.Source = new BitmapImage(uriSource);
_selected = true;
}
}
This works but is very cumbersome and I will have up to 20 check boxes.
How can I create a custom XAML Style that I can use for each image or something similar.
EDIT:
I have used the following style to handle the hover over:
<Page.Resources>
<Style TargetType="Image" x:Key="checkBoxStyle">
<Setter Property="Source" Value="/Images/checkcircle_off.png"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/Images/checkcircle_hover.png"/>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<Image x:Name="Image_Custom" Style="{StaticResource checkBoxStyle}" Width="16" Height="16" HorizontalAlignment="Left" Margin="30,107,0,0" VerticalAlignment="Top" MouseEnter="Image_Custom_MouseEnter" MouseLeave="Image_Custom_MouseLeave" MouseUp="Image_Custom_MouseUp" MouseLeftButtonDown="Image_Custom_MouseLeftButtonDown"/>
But I dont know how to handle the clicked event. How can I do this?
EDIT 2
I have did the following:
<Style TargetType="{x:Type CheckBox}" x:Key="myCheckBoxStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image x:Name="checkBoxImage" Source="/Images/checkcircle_off.png"></Image>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="checkBoxImage" Property="Source" Value="/Images/checkcircle_on.png"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="checkBoxImage" Property="Source" Value="/Images/checkcircle_off.png"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="checkBoxImage" Property="Source" Value="/Images/checkcircle_hover.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<CheckBox Content="My CheckBox" Style="{StaticResource myCheckBoxStyle}" Width="16" Height="16" Foreground="white" FontSize="16" HorizontalAlignment="Left" Margin="30,242,0,0" VerticalAlignment="Top" />
The correct images appear when hovered, checked and unchecked.
But I noticed that the Content has disappeared ("My Checkbox") and also I only want the hover state to appear when its not checked, how can I do that?
In WPF you generally look for a control that has the functionality you need and then you make it look like you want. So if you want CheckBox functionality then you use CheckBox control and change its Template to be what you want. So you can create Style for CheckBox that will set your custom Template
<Window.Resources>
<Style TargetType="{x:Type CheckBox}" x:Key="myCheckboxStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Image x:Name="checkboxImage" Source="normal.png" Width="32"/>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="checkboxImage" Property="Source" Value="checked.png"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="False"/>
</MultiTrigger.Conditions>
<Setter TargetName="checkboxImage" Property="Source" Value="hover.png"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
and then just use it on any CheckBox
<CheckBox Style="{StaticResource myCheckboxStyle}" Content="ABC"/>
and you'll have all CheckBox functionality with your custom looks
If you override CheckBox and create a specific style this will look like this:
public class MyCheckBox : CheckBox
{
#region ImageNormal
/// <summary>
/// ImageNormal Dependency Property
/// </summary>
public static readonly DependencyProperty ImageNormalProperty =
DependencyProperty.Register("ImageNormal", typeof(ImageSource), typeof(MyCheckBox),
new FrameworkPropertyMetadata((ImageSource)null));
/// <summary>
/// Gets or sets the ImageNormal property. This dependency property
/// indicates ....
/// </summary>
public ImageSource ImageNormal
{
get { return (ImageSource)GetValue(ImageNormalProperty); }
set { SetValue(ImageNormalProperty, value); }
}
#endregion
#region ImageChecked
/// <summary>
/// ImageChecked Dependency Property
/// </summary>
public static readonly DependencyProperty ImageCheckedProperty =
DependencyProperty.Register("ImageChecked", typeof(ImageSource), typeof(MyCheckBox),
new FrameworkPropertyMetadata((ImageSource)null));
/// <summary>
/// Gets or sets the ImageChecked property. This dependency property
/// indicates ....
/// </summary>
public ImageSource ImageChecked
{
get { return (ImageSource)GetValue(ImageCheckedProperty); }
set { SetValue(ImageCheckedProperty, value); }
}
#endregion
//... other image properties removed for simplicity
static MyCheckBox()
{
//Override base class style
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCheckBox), new FrameworkPropertyMetadata(typeof(MyCheckBox)));
}
}
Associated XAML Style:
<Style TargetType="{x:Type local:MyCheckBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="grdNormal">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="grdMouseOver">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="grdPressed">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="grdNormal">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgUnchecked1">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgChecked1">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgUnchecked2">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgChecked2">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgUnchecked3">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="imgChecked3">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked"/>
<VisualState x:Name="Indeterminate"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="grdNormal">
<Image x:Name="imgUnchecked1" Source="{Binding ImageNormal, RelativeSource={RelativeSource TemplatedParent}}"/>
<Image x:Name="imgChecked1" Visibility="Collapsed" Source="{Binding ImageNormal, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
<Grid x:Name="grdMouseOver" Visibility="Collapsed">
<Image x:Name="imgUnchecked2" Source="{Binding ImageMouseOver, RelativeSource={RelativeSource TemplatedParent}}"/>
<Image x:Name="imgChecked2" Visibility="Collapsed" Source="{Binding ImageMouseOverChecked, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
<Grid x:Name="grdPressed" Visibility="Collapsed">
<Image x:Name="imgUnchecked3" Source="{Binding ImagePressed, RelativeSource={RelativeSource TemplatedParent}}"/>
<Image x:Name="imgChecked3" Visibility="Collapsed" Source="{Binding ImagePressedChecked, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Visual States and a custom dependency property (MVVM)

I'm stuck trying to add a dependency property to a button. I have several buttons located in my header view and clicking on them changes the content in the ContentControl between different views. All this works great. I want the button that was clicked have a different forecolor than the others and it looks like I need to add a dependency property. I think I have all the pieces in place but can't figure out how to get them all to work together.
I have a string property named ViewState in my viewmodel which changes based upon the button being clicked. The property is changing and I'm calling RaisePropertyChanged when it happens. What do I need to do to bind the additional dependency property? I'm transitioning from the WinForm world and trying to mentally piece it all together but struggling a bit.
Here's what I have so far:
<Style TargetType="{x:Type Button}" x:Key="LocalButtonTemplate">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="outerBorder" Background="{TemplateBinding Background}" Margin="4">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewState">
<VisualState x:Name="Dashboard">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Yellow"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="AccountTables">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Red"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="Purple"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" To="#35A84D"
Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="contentPresenter"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Border x:Name="Background" BorderBrush="Transparent">
<Grid>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4"/>
</Grid>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My buttons:
<dxwuii:SplitPanel Margin="0,10,10,10" HorizontalAlignment="Right" Grid.Column="2" ItemSpacing="0" Orientation="Horizontal" ItemSizeMode="AutoSize" >
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="AccountTablesViewModel" Style="{StaticResource LocalButtonTemplate}">Express Tables</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="MappingViewModel" Style="{StaticResource LocalButtonTemplate}">Item Mapping</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="ReportsViewModel" Style="{StaticResource LocalButtonTemplate}">Reports</Button>
<Button Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="PostBalancesViewModel" Style="{StaticResource LocalButtonTemplate}">Post Balances</Button>
</dxwuii:SplitPanel>
Dependency Property Class:
namespace MyAppName.Model
{
public class StateManager : DependencyObject
{
public static string GetVisualStateProperty(DependencyObject obj)
{
return (string)obj.GetValue(VisualStatePropertyProperty);
}
public static void SetVisualStateProperty(DependencyObject obj, string value)
{
obj.SetValue(VisualStatePropertyProperty, value);
}
public static readonly DependencyProperty VisualStatePropertyProperty =
DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(StateManager),
new PropertyMetadata((dependencyObject, args) =>
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
return;
VisualStateManager.GoToState(frameworkElement, (string)args.NewValue, true);
}));
}
}
Set Tag and Attached property on your each button as below. Tag value will be the VisualState value to which button should go on click.
<Button Tag="AcountTables" local:StateManager.VisualStateProperty="{Binding YOURVIEWMODELPROPERTY}" Command="{Binding SendViewModelNameCommand, Mode=OneTime}" CommandParameter="AccountTablesViewModel" Style="{StaticResource LocalButtonTemplate}">Express Tables</Button>
Update your AttachedProperty like:
public static readonly DependencyProperty VisualStatePropertyProperty =
DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(StateManager),
new PropertyMetadata((dependencyObject, args) =>
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
return;
if (args.NewValue == frameworkElement.Tag.ToString())
{
VisualStateManager.GoToState(frameworkElement, (string)args.NewValue, true);
}
else
{
VisualStateManager.GoToState(frameworkElement, "Normal", true);
}
}));

Categories