How to do binding in WPF - C# - wpf

I have following code:
<ControlTemplate x:Key="ViewItemTemplate"
TargetType="ListViewItem">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="0,0,3,0" x:Name="CkBox">
<CkBox.IsChecked>
<Binding Path="IsSelected"
Mode="TwoWay">
<Binding.RelativeSource>
<RelativeSource Mode="TemplatedParent" />
</Binding.RelativeSource>
</Binding>
</CkBox.IsChecked>
<DataTrigger Binding="{Binding InvalidForeground}" Value="true">
<Setter TargetName="CkBoxVisual" Property="Foreground" Value="#999999"/>
</DataTrigger>
</CheckBox>
<ContentPresenter />
</StackPanel>
</ControlTemplate>
How can i bind InvalidForeground? I looked online for many example they tell to use DataTemplate. But when i add DataTemplate above StackPanel i get errors? Am i doing something wrong?
I am trying to bind InvalidForeground so i can add some code to it. I am getting an error: Cannot resolve symbol 'InvalidForeground' due to unknown DataContext.

It appears you are trying to declare a customized checkbox control to use within WPF application. As such, your "InvalidForeground" property would be exposed, but the template doesn't understand what its real type is expected.
I've posted another answer here which gave a full step-by-step for a custom button. The principles are the same, and I've tried to point out my understanding of of the declarations, the type, etc. Hopefully it can guide you on not just this, but other class templates too.

<ControlTemplate x:Key="ViewItemTemplate"
TargetType="ListViewItem">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="0,0,3,0" x:Name="CkBox">
<CkBox.IsChecked>
<Binding Path="IsSelected"
Mode="TwoWay">
<Binding.RelativeSource>
<RelativeSource Mode="TemplatedParent" />
</Binding.RelativeSource>
</Binding>
</CkBox.IsChecked>
<DataTrigger Binding="{Binding InvalidForeground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Value="true">
<Setter TargetName="CkBoxVisual" Property="Foreground" Value="#999999"/>
</DataTrigger>
</CheckBox>
<ContentPresenter />
</StackPanel>
</ControlTemplate>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
abc A = new abc();
A.InvalidForeground = true;
}
}
public class abc : INotifyPropertyChanged
{
private bool invalidForeGround;
public bool InvalidForeground
{
get
{ return invalidForeGround; }
set
{
invalidForeGround = value;
Notify("InvalidForeground");
}
}
private void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
InvalidForeground must be the property in the DataContext of the Control whose template the above code is.I hope this will help

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>

Data Trigger binding another element in different visual tree node

How to modify style of one element in a visual tree, based on style in triggers in another element in different visual tree node
For example,I am having a list of colors,
ColorList = new List<ColorViewModel>();
ColorList.Add(new ColorViewModel() { ColorCode = "#FF0000", ColorName="Red" });
ColorList.Add(new ColorViewModel() { ColorCode = "#00FF00", ColorName="Green" });
ColorList.Add(new ColorViewModel() { ColorCode = "#0000FF", ColorName="Blue" });
this.DataContext = this;
I have the colors show in a ItemsControl and their name in another ItemsControl, When I hover on their name, I want to increase the size of color box for the corresponding color.
I tried setting the triggers based on element name, but since the scope is different. The following is the sample code, that covers my complex scenario. Is there a xaml way to overcome this? Any help appreciated.
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding ColorList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="20" Height="20" Fill="{Binding ColorCode}">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ColorName, Path=IsMouseOver}" Value="True">
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="30"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding ColorList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="ColorName" Text="{Binding ColorName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
It would be possible using XAML only, if UIElement.IsMouseOver would have a setter. Since it is read-only it can't be target of a Binding. That IsMouseOver is read-only makes perfect sense, as it is intended to be set internally solely on mouse input.
Because of this, it is required to either extend ListBox (or ItemsControl) or to implement an attached behavior.
In order to transport the IsMouseOver flag information between both controls, you can add a dedicated property to the data model. The data model (or DataContext) is the only link between both item controls.
In the following example, this property is expected to be of the following definition:
private bool isPreviewEnabled;
public bool IsPreviewEnabled
{
get => this.isPreviewEnabled;
set
{
this.isPreviewEnabled = value;
OnPropertyChanged(nameof(this.IsPreviewEnabled));
}
}
Note that the model should implement INotifyPropertyChanged.
The following example is an attached behavior, that delegates the mouse over flag by providing an attached property as binding target.
Element.cs
public class Element : DependencyObject
{
#region IsMouseOver attached property
public static readonly DependencyProperty IsMouseOverElementProperty = DependencyProperty.RegisterAttached(
"IsMouseOver",
typeof(bool?),
typeof(Element),
new FrameworkPropertyMetadata(
default(bool?),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnBindngAttached));
public static void SetIsMouseOver(DependencyObject attachingElement, bool? value) => attachingElement.SetValue(Element.IsMouseOverProperty, value);
public static bool? GetIsMouseOver(DependencyObject attachingElement) => (bool) attachingElement.GetValue(Element.IsMouseOverProperty);
#endregion
private static void OnBindngAttached(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Only listen to mouse events when the binding initializes the property.
// This guarantees single subscription.
if (d is FrameworkElement frameworkElement && e.OldValue == null)
{
frameworkElement.MouseEnter += DelegateIsMouseEnter;
frameworkElement.MouseLeave += DelegateIsMouseLeave;
}
}
private static void DelegateIsMouseEnter(object sender, MouseEventArgs e)
{
var attachedElement = sender as DependencyObject;
SetIsMouseOver(attachedElement, true);
}
private static void DelegateIsMouseLeave(object sender, MouseEventArgs e)
{
var attachedElement = sender as DependencyObject;
SetIsMouseOver(attachedElement, false);
}
}
MainWindow.xaml
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding ColorList}">
<ItemsControl.ItemTemplate>
<DataTemplate">
<Rectangle Fill="{Binding ColorCode}">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Width" Value="20" />
<Setter Property="Height" Value="20" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsPreviewEnabled}" Value="True">
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="30" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding ColorList}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<!--
Delegate the IsMouseOver to the data model,
which is the data context of the item container
-->
<Setter Property="Element.IsMouseOver"
Value="{Binding IsPreviewEnabled}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ColorName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>

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.

WPF Custom Control library : trying to bind to templatedparent property from a keyed resource, ancestor way fails

How can I make this custom control library binding work? The code is heavily simplified for the sake of brevity, the Listview chooses its current View among several Views thank to a Datatrigger in its style, I'm only showing one for the sake of brevity. The failing binding is the GridViewColumn.Width one
Themes/Generic.Xaml :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<DataTemplate x:Key ="myDataTemplate">
<!--This visibility binding works-->
<CheckBox Width="16" Height="16" x:Name="ItemCheckbox"
Visibility="{Binding CheckBoxVisibility,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:CustomControl1}}}" />
</DataTemplate>
<GridView x:Name="myView" x:Key="myView" x:Shared="False">
<!--this width binding fails : Cannot find source for binding-->
<GridViewColumn x:Name="NameColumn" Header="Name"
CellTemplate="{StaticResource myDataTemplate}"
Width="{Binding NameColumnWidth,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:CustomControl1}}}" />
</GridView>
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<ListView ItemsSource="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ItemsSource}" >
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ViewDetails}" Value="True">
<Setter Property="View"
Value="{StaticResource myView}" />
</DataTrigger>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ViewDetails}" Value="False ">
<Setter Property="View"
Value="{StaticResource myView2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
CustomControl1.cs
public class CustomControl1 : Control
{
//these properties should be DependencyProperties, simplified here for the sake of brevity
ObservableCollection<Item> items;
public object ItemsSource { get => items; }
//this is the property I fail to bind to
public double NameColumnWidth { get => this.Width; }
//this one works
public Visibility CheckBoxVisibility { get => Visibility.Visible; }
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1),
new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
}
Edit:
I actually ended up changing my approach and instead of binding the column width to a Property in the templated parent I made it auto resizing to its content by subclassing GridView. This is an overall better solution. If anyone finds a solution to this binding puzzle I'll still be happy to test and accept it.
public class AutoAdjustingGridView: GridView
{
protected override void PrepareItem(ListViewItem item)
{
base.PrepareItem(item);
foreach(var column in Columns)
{
if (double.IsNaN(column.Width))
column.Width = column.ActualWidth;
column.Width = double.NaN;
}
}
}

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 .

Resources