Bind to HierarchicalDataTemplate DataContext from nested DataTemplate - wpf

I have my treeview with 2 level nested levels.
<TreeView ItemsSource="{Binding Project.Tables}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type TableModel}" ItemsSource="{Binding Columns}">
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ColumnModel}">
</DataTemplate>
</TreeView.Resources>
</TreeView>
My binding source with tables:
public class ProjectModel : PropertyChangedBase
{
private BindableCollection<TableModel> _tables;
public BindableCollection<TableModel> Tables
{
get { return _tables; }
set
{
if (_tables != value)
{
_tables = value;
NotifyOfPropertyChange();
}
}
}
}
public class TableModel
{
public BindableCollection<ColumnModel> Columns { get; set; }
public bool IsSelected
{
get;
}
}
I tried to use next binding:
<DataTemplate DataType="{x:Type projectModels:ColumnModel}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=DataContext.IsSelected }" Value="True">
<Setter Property="Foreground" Value="{StaticResource GreyBrush3}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock Text="{Binding ColumnName}">
</TextBlock>
</StackPanel>
</DataTemplate>
Inside DataTemplate I want to have binding to DataContext of HierarchicalDataTemplate. I already tried different AncestorLevels and it did not work, any ideas how can I do it?

Related

WPF Bound ListView Double Click event

I'm new to WPF so please forgive any of the below that doesn't make sense...
I've set up a view model, which is bound to a WPF List view data template. The source objects (belonging to a class called BrowserItem) are in an ObservableCollection, and I use a CollectionViewSource which diplays them in the ListView - after several headaches this works really well.
I thought the simplest part would be the handling when a user double clicks on something, but I was wrong.
The Event attached to the ListView returns the TextBlock control rather than the BrowserItem source object that was clicked on.
Can anyone point me to a simple way of obtaining the original item (BrowserItem) when the control is clicked on - I've seen several ways of doing this, but they all seem very complex to my simple mind.
A simple example would be great.
This is the XAML for the list view if its of use:
<ListView Name="ViewList"
ItemsSource="{Binding GroupItems}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Expander>
<Expander.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Children.Count}"
Value="0">
<Setter Property="Expander.Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<ListBox ItemsSource="{Binding Children}" />
</Expander>
<TextBlock Text="{Binding Name}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Active}"
Value="true">
<Setter Property="Background"
Value="YellowGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander>
<Expander.Header>
<TextBlock Text="{Binding Name}" />
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
Thanks.
You can use EventSetter to add MouseDoubleClick event to listview like below:
<ListView ItemsSource="{Binding Source}" SelectedItem="{Binding SelectedModel, Mode=TwoWay}">
<!--<Behaviors:Interaction.Triggers>
<Behaviors:EventTrigger EventName="MouseDoubleClick">
<Behaviors:InvokeCommandAction Command="{Binding DataContext.Command, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" />
</Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>-->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Model}">
<TextBlock Text="{Binding Age}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In this way you just handle it in code behand , not in ViewModel.
If you want handle it in viewmodel, you need one class like this:
public class EventToCommand
{
public static string GetEventName(DependencyObject obj)
{
return (string)obj.GetValue(EventNameProperty);
}
public static void SetEventName(DependencyObject obj, string value)
{
obj.SetValue(EventNameProperty, value);
}
public static readonly DependencyProperty EventNameProperty =
DependencyProperty.RegisterAttached("EventName", typeof(string), typeof(EventToCommand), new PropertyMetadata("", OnEventNameChanged));
public static void OnEventNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(EventToCommand), new PropertyMetadata(OnCommandChanged));
public static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as FrameworkElement;
EventInfo eventInfo = d.GetType().GetEvent(GetEventName(d));
var eventHandlerMethodInfo = typeof(EventToCommand).GetMethod("Invoke", BindingFlags.Static | BindingFlags.Public);
eventInfo.AddEventHandler(d, Delegate.CreateDelegate(eventInfo.EventHandlerType, eventHandlerMethodInfo));
}
public static void Invoke(object sender,EventArgs e)
{
var command = GetCommand(sender as FrameworkElement);
command.Execute(e);
}
}
And use class EventToCommand like this:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="local:EventToCommand.EventName" Value="MouseDoubleClick" />
<Setter Property="local:EventToCommand.Command" Value="{Binding DataContext.Command, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" />
</Style>
</ListView.ItemContainerStyle>

TabControl view doesn't update on template change

I need to change the view of a TabControl's content on-the-fly.
I am guessing the best way to accomplish this is to define the view as a DataTemplate, and then change said template using a trigger.
In my test app, the background color is tied to the same data trigger as the template. The background color updates immediately upon making the radio button selection.
Expected behavior: The Tab Item Content / DataTemplate also updates immediately.
Actual Behavior: Tab content view does not update until the tab selection is changed.
Here's my Minimal, Complete, and Verifiable example:
Window XAML
<Window x:Class="ChangeView.Window1"
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"
Title="Window1" Height="350" Width="400">
<Window.Resources>
<DataTemplate x:Key="ContentTemplate1">
<Grid>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding MyBlurb}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ContentTemplate2">
<Grid>
<Label HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Content="{Binding MyHeader}" Background="Black" Foreground="White" FontSize="72"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType1}" Value="False">
<Setter Property="Background" Value="Chartreuse"/>
</DataTrigger>
<DataTrigger Binding="{Binding ViewType1}" Value="True">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Margin="10,38,0,0" Text="Content Template:"/>
<RadioButton x:Name="radio1" Margin="120,40,0,0" Grid.ColumnSpan="2" Content="1" GroupName="ViewSelect" IsChecked="{Binding Path=ViewType1}"/>
<RadioButton Margin="170,40,0,0" Grid.ColumnSpan="2" Content="2" GroupName="ViewSelect"/>
<TabControl Grid.Row="1" ItemsSource="{Binding TabGroup}">
<TabControl.Style>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Margin" Value="10"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType1}" Value="True">
<Setter Property="ContentTemplate" Value="{DynamicResource ContentTemplate1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ViewType1}" Value="False">
<Setter Property="ContentTemplate" Value="{DynamicResource ContentTemplate2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.ItemTemplate>
<DataTemplate>
<Border x:Name="headerBorder">
<Label Content="{Binding MyHeader}" FontSize="20"/>
</Border>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
Code Behind
namespace ChangeView
{
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
public ObservableCollection<TabData> TabGroup { get; set; } = new ObservableCollection<TabData>();
private bool _viewType1 = true;
public bool ViewType1
{
get { return _viewType1; }
set { _viewType1 = value; RaisePropertyChanged(nameof(ViewType1)); }
}
public Window1()
{
TabGroup.Add(new TabData("♻️", "Recycle"));
TabGroup.Add(new TabData("⚔", "Swords"));
TabGroup.Add(new TabData("⚗", "Chemistry"));
TabGroup.Add(new TabData("🌵", "Cactus"));
TabGroup.Add(new TabData("👺", "Tengu"));
TabGroup.Add(new TabData("🐙", "Octopus"));
DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class TabData : INotifyPropertyChanged
{
private string _myHeader, _myBlurb;
public TabData(string header, string blurb)
{
MyHeader = header;
MyBlurb = blurb;
}
public string MyHeader
{
get { return _myHeader; }
set { _myHeader = value; RaisePropertyChanged(nameof(MyHeader)); }
}
public string MyBlurb
{
get { return _myBlurb; }
set { _myBlurb = value; RaisePropertyChanged(nameof(MyBlurb)); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
After changing the radio button state, change the selected tab. You will then see the correct content template.
It looks as if, in a TabControl, changing the content template alone does not cause the content to be rendered. If you render new content by switching the selected tab, the current content template will then be used.
So let's write one ContentTemplate, which creates a ContentControl and switches the ContentControl's ContentTemplate. I've tested, and the ContentControl will re-render its content when its ContentTemplate changes. The bindings get a little bit verbose.
<TabControl ItemsSource="{Binding TabGroup}" Grid.Row="1">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl
x:Name="ContentCtl"
Content="{Binding}"
/>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding DataContext.ViewType1, RelativeSource={RelativeSource AncestorType=TabControl}}"
Value="True">
<Setter
TargetName="ContentCtl"
Property="ContentTemplate"
Value="{DynamicResource ContentTemplate1}"
/>
</DataTrigger>
<DataTrigger
Binding="{Binding DataContext.ViewType1, RelativeSource={RelativeSource AncestorType=TabControl}}"
Value="False"
>
<Setter
TargetName="ContentCtl"
Property="ContentTemplate"
Value="{DynamicResource ContentTemplate2}"
/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemTemplate>
<DataTemplate>
<Border x:Name="headerBorder">
<Label Content="{Binding MyHeader}" FontSize="20"/>
</Border>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
You could also do something ugly in your code behind to make the TabControl render itself again on command. Or maybe you can replace the metadata on TabControl.ContentTemplate.

Binding to DataGridColumnHeader.Command

I need to use Command on DataGridColumnHeader to sort my data. Standard DataGrid sorting feature is not enough for me because there are not all data displayed in DataGrid. I don't even have all data present in my VM. (It's not possible... it's too much) I just request concrete page of data from server. And now I would also like to get concrete page from sorted data.
So I did this:
<DataGrid ItemsSource="{Binding Path=Entities, Mode=OneWay}" CanUserSortColumns="False" SelectionMode="Single" SelectedItem="{Binding Path=SelectedEntity}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn >
<DataGridTemplateColumn.Header>
<DataGridColumnHeader Content="Column1" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Property1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
It works great. And now I just want to have AutoGenerateColumns="True" unfortunately it works no more. Is there anybody who can explain why it doesn't work for autogenerated columns and provide me with some solution? Thank you in advance!
EDIT
It has probably something to do with the fact that following doesn't work either.
<DataGrid ItemsSource="{Binding Path=Entities, Mode=OneWay}" CanUserSortColumns="False" SelectionMode="Single" SelectedItem="{Binding Path=SelectedEntity}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<!-- HERE IS THE CHANGE -->
<DataGridTemplateColumn.Header>Column1</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Property1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
But style is applied. I know this because I tried:
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Tomato"/> <!-- beautiful tomato background -->
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
Column header has Tomato background, but command doesn't work.
EDIT2
Following is the solution. Apparently DataGridColumnHeaders doesn't inherit DataContext, because when I change the command binding everything works again.
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=DataContext.MyCommand}"/>
But still I don't quite understand why. So answer with detailed explanation will get the bounty.
DataGridColumnHeader is container item for DataGridColumn.Header same as, for example, ListBoxItem is container item for each item in ListBox's ItemsSource.
Since ListBoxItem's DataContext will be set to an item from ListBox's ItemsSource, it is natural that DataGridColumnHeader's DataContext will be set to DataGridColumn.Header object.
As your findings suggest, autogenerated columns will have a DataGridColumn.Header with its DataContext set to the string header object.
If you do not use autogenerated columns, and instead specify a DataGridTemplateColumn and the DataGridTemplateColumn.Header, standard inheritance for the DataContext still applies, so that's why it worked for you the first time.
Edit: A quick search didn't provide immediate results where this behavior is described. However, verifying the fact is not too hard:
Xaml:
<DataGrid ItemsSource="{Binding MyClasses}" AutoGenerateColumns="True" MouseRightButtonUp="DataGrid_MouseRightButtonUp_1" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<DataGridColumnHeader Content="Name (Manual Column)" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Class1> _myClasses;
public ObservableCollection<Class1> MyClasses { get { return _myClasses; } set { _myClasses = value; OnPropertyChanged("MyClasses"); } }
public ViewModel()
{
MyClasses = new ObservableCollection<Class1>()
{
new Class1() { Name = "Andy" },
new Class1() { Name = "Mark" },
new Class1() { Name = "Peter" },
new Class1() { Name = "Gregor" },
new Class1() { Name = "Jenny" }
};
}
}
Code behind (for illustration purpose only):
private void DataGrid_MouseRightButtonUp_1(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is Microsoft.Windows.Themes.DataGridHeaderBorder)
{
object dataContext = ((Microsoft.Windows.Themes.DataGridHeaderBorder)e.OriginalSource).DataContext;
}
}
Right-click on the manual column header (outside the textblock) produces the following dataContext:
Right-click on the autogenerated column header:

DatagridTemplateColumn with triggers failed to bind

I have a datagrid that contain of the 2 columns and I would like to show some of cells in column#2 to be:
ComboBox
TextBox
based on property
code :
Solution #1 :
<Window.Resources>
<DataTemplate x:Key="DropDownTemplate">
<StackPanel>
<ComboBox SelectedValuePath="Id" DisplayMemberPath="Name" ItemsSource="{Binding MarketConfigurationLOVs, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TextBoxTemplate">
<StackPanel>
<TextBox Text="{Binding ConfigurationValue, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
Here the datagrid tags :
<DataGrid ItemsSource="{Binding Path= MarketConfigurationValues,Mode=TwoWay}" HeadersVisibility="None" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Width="120" Binding="{Binding Path= ConfigurationName}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" >
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding HasLOV}" Value="true">
<Setter Property="ContentTemplate" Value="{StaticResource DropDownTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding HasLOV}" Value="false">
<Setter Property="ContentTemplate" Value="{StaticResource TextBoxTemplate}" />
</DataTrigger>
<!-- and so on -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
this failed to show actual value as it show the name of property and always show cell in dropdown and when I swapped the TextBoxContentTemplate with ComboBoxContentTemplate it show all cells as textBox so it seem it ignore the trigger however when I debug I found the HasLOV some items contain true and some contain false
Solution #2 : (also failed) Reference : Jon solution from the following post WPF MVVM Creating Dynamic controls
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl x:Name="MyContentControl" Content="{Binding}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding HasLOV}" Value="false">
<Setter TargetName="MyContentControl" Property="ContentTemplate" Value="{StaticResource TextBoxTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding HasLOV}" Value="true">
<Setter TargetName="MyContentControl" Property="ContentTemplate" Value="{StaticResource DropDownTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
ViewModel is big class but I pulled here the main parts related to my problem . In constructor I created list of MarketConfigurationValue and I filled values inside MarketConfigurationLOVs in case if I set HasLov=true and I filled value in configurationValue in case if I set HasLov=false and I run the application and in debug mode I found the main object List contain what I said correctly but it failed to show comboBox in case HasLov=true and textBox in case HasLov=false :
public class MarketConfigurationValue
{
public string ConfigurationName { get; set; }
public string ConfigurationValue { get; set; }
public int ConfigurationValueId { get; set; }
public List<Market_Config_Lov> MarketConfigurationLOVs {get;set;}
public bool HasLov { get; set; }
}
public class Market_Config_Lov
{
public virtual string Name { get; set; }
public virtual int Sequence { get; set; }
}
Can you please help ?
Here's how you implement the INotifyPropertyChanged Interface. With this the binding updates itself when the setter of HasLOV is called.
public class MarketConfigurationValue:INotifyPropertyChanged
{
private bool _hasLOV;
public bool HasLOV
{
get {return _hasLOV;}
set {_hasLOV = value; OnPropertyChanged("HasLOV");}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Hope this helps.
====>The property name is typo as the property in class called : HasLov and in XAML called HasLOV .

WPF Change ViewModel Property in View

I have a WPF DataGrid with several Templated Columns containing Checkboxes.
If I press a specific Checkbox the source of another checkbox has to be set.
I have a Viewmodel with a Property called Property (yes i know :)), and this Property has the Property "Visible".
If I check the "Mandatory" checkbox I want the "Visible" Value to be set and so the visible Checkbox to be set too, but somehow this is not working.
Heres my code:
<toolkit:DataGridTemplateColumn Header="Mandatory" IsReadOnly="False">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<CheckBox IsChecked="{Binding Path=Mandatory,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MandatoryDB}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MandatoryDB}" Value="False">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<ei:ChangePropertyAction TargetObject="{Binding UpdateSourceTrigger=PropertyChanged}" PropertyName="ReadOnly" Value="False" />
<ei:ChangePropertyAction TargetObject="{Binding NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" PropertyName="Visible" Value="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
Thanks,
Jonny
I think what you're trying to do is to drive your business logic by your View which is a horrible anti-pattern. What you need to is when the value of the "Mandatory" checkbox is changed then your viewmodel needs to set other properties accordingly and alert your view that there was a change (INotifyPropertyChanged).
Your view should not set any viewmodel properties, it should only read them and pass user input, the only other things which your view needs to controll are converters and other view-only items.
Hope this helps
see if this can help you.
lets assume that you want to show yes, no and ans in your Datagrid. for this find the sample code below.
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Coll}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Yes">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="50"
Height="50"
IsChecked="{Binding Yes,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="No">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="50"
Height="50"
IsChecked="{Binding No,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Ans">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle Width="100"
Height="50"
RadiusX="25"
RadiusY="25">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Ans}" Value="true">
<Setter Property="Fill" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Ans}" Value="false">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
code behind
public partial class YesNoTest : Window
{
public YesNoTest()
{
InitializeComponent();
if (Coll == null)
{
Coll = new ObservableCollection<Anss>();
for (int index = 0; index < 10; index++)
{
Coll.Add(new Anss() { Yes = false, No = false, Ans = true });
}
}
this.DataContext = this;
}
private ObservableCollection<Anss> _coll;
public ObservableCollection<Anss> Coll
{
get { return _coll; }
set { _coll = value; }
}
}
public class Anss : INotifyPropertyChanged
{
private bool _Yes;
public bool Yes
{
get
{
return _Yes;
}
set { _Yes = value; OnChanged("Yes"); OnChanged("Ans"); }
}
private bool _No;
public bool No
{
get
{
if (_No == true)
Yes = false;
return _No;
}
set { _No = value; OnChanged("No"); OnChanged("Ans"); }
}
private bool _Ans;
public bool Ans
{
get
{
if (Yes == true)
_Ans = true;
else
_Ans = false;
return _Ans;
}
set { _Ans = value; OnChanged("Ans"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}

Resources