WPF DataTrigger not firing on NotifyProperyChanged? - c#

I'm trying to dynamically hide elements in my ComboBox based on a boolean flag in the objects bound it it but I can't seem to get it working
Here is me XAML
<xctk:CheckComboBox Name="TagsDropDown" HorizontalAlignment="Left" Height="30" Margin="0,0,0,0" VerticalAlignment="Top" Width="450" IsEditable="True" IsTextSearchEnabled="True" ItemsSource="{Binding AllTags}" ItemSelectionChanged="TagsDropDown_OnItemSelectionChanged">
<xctk:CheckComboBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<TextBlock Margin="2" Text="{Binding Name}"/>
<TextBlock Margin="2" HorizontalAlignment="Right" Text="{Binding Count}"/>
</Grid>
</DataTemplate>
</xctk:CheckComboBox.ItemTemplate>
<xctk:CheckComboBox.ItemContainerStyle>
<Style TargetType="xctk:SelectorItem">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<!-- <DataTrigger steamTools:TriggerTracing.TriggerName="is zero" steamTools:TriggerTracing.TraceEnabled="True" Binding="{Binding Path=IsZero, RelativeSource={RelativeSource Self} }" Value="True">-->
<DataTrigger Binding="{Binding Path=IsZero, RelativeSource={RelativeSource Self} }" Value="True">
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:CheckComboBox.ItemContainerStyle>
</xctk:CheckComboBox>
And the class bound to the control
public class CompTag : INotifyPropertyChanged
{
private int _count;
private bool _isZero;
public string Name { get; set; }
public bool IsZero
{
get { return _isZero; }
set
{
if (_isZero != value)
{
_isZero = value;
OnPropertyChanged("IsZero");
}
}
}
public int Count
{
get { return _count; }
set
{
if (_count != value)
{
_count = value;
OnPropertyChanged("Count");
}
}
}
Can anyone see what I am doing wrong?
Thanks in advance!

Does your AllTags list have isZero member?(in model )
if yes
Try This
Binding="{Binding Path=IsZero, {RelativeSource FindAncestor, AncestorType={x:Type xctk:CheckComboBox}} }"
if no, and it in your datacontext class try to remove RelativeSource={RelativeSource Self}

Related

wpf mvvm - Is there a way to programmatically create textbox based on number of properties in model and bind to each of them in?

I have multiple objects with multiple properties which I want to display or hide (or create if needed) based on the property value.
For example:
Model:
public class InputModel
{
public string Name { get; set; }
public string Id { get; set; }
public string Type { get; set; }
public bool Required { get; set; }
public int Dpi { get; set; }
public string Data { get; set; }
public List<ColorModel> Filter { get; set; }
public List<RenderModel> Render { get; set; }
public List<LayoutModel> Layout { get; set; }
}
View:
<ItemsControl x:Name="InputsList"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="400">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" Background="Beige" Margin="10">
<TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
<TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
<TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
<CheckBox Grid.Column="1" Content="Required" Margin="0,5,0,0"
IsChecked="{Binding ElementName=Required, Path=CheckBoxIsChecked}"/>
<TextBox Grid.Column="0" Grid.Row="4"
Text="{Binding Dpi, StringFormat='Dpi: {0}'}"/>
<TextBlock Grid.Column="0" Grid.Row="5"
Text="Render:"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And my ViewModel:
public class InputsListViewModel : Screen
{
private IRootModel _rootModel;
public InputsListViewModel(IRootModel rootModel)
{
_rootModel = rootModel;
}
private BindingList<InputModel> _inputs;
public BindingList<InputModel> InputsList
{
get
{
_inputs = new (_rootModel.Inputs);
return _inputs;
}
set
{
_inputs = value;
NotifyOfPropertyChange(() => InputsList);
}
}
private BindingList<RenderModel> _renders;
public BindingList<RenderModel> RenderList
{
get
{
_renders = new(_rootModel.Render);
return _renders;
}
set
{
_renders = value;
NotifyOfPropertyChange(() => RenderList);
}
}
private BindingList<LayoutModel> _layouts;
public BindingList<LayoutModel> LayoutList
{
get
{
_layouts = new (_rootModel.Layout);
return _layouts;
}
set
{
_layouts = value;
NotifyOfPropertyChange(() => LayoutList);
}
}
}
I don't need to display all of the properties in here, just the ones that I need at a given time. And I want to be able to add any of the properties that are missing, if I need to.
I am a beginner and so far I only know how to manually create the fields that I need in view and bind to them.
Thanks!
you can hide whole items with ItemContainerStyle or individual Properties as shown in 2 different ways
<ItemsControl ItemsSource="{Binding MyItems}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Visibility" Value="{Binding ShowInUi, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
<TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
<TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
<!--possibility 1-->
<TextBlock x:Name="AdditionalInfoText" Text="{Binding AdditionalInfo}"/>
<!--possibility 2-->
<TextBlock Text="{Binding SuperDuperValue}" Visibility="{Binding IsSuperDuperItem, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding AdditionalInfo}" Value="{x:Null}">
<Setter TargetName="AdditionalInfoText" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For all textblocks in an area
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<DataTrigger Binding="{Binding Text.Length, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>

How to bind backgroundcolor of WPF listview items?

I am new to WPF and I have read a lot of similar questions on the web, but I still do not get my listview work. I want to change the background color of a list view element depending of a property (red, yellow or green)
The itemsSource of my ListView is an observable list of this class:
public class ConnectionItem
{
public ConnectionItem(string name)
{
Name = name;
}
public string Name { get; }
private string _color = "Red";
public string Color { get => _color; }
private ConnectionStatus _status;
public ConnectionStatus Status
{
set
{
if (value == _status)
{
return;
}
else
{
switch (value)
{
case ConnectionStatus.Connected:
_color = "Yellow";
break;
case ConnectionStatus.Ready:
_color = "Green";
break;
default:
_color = "Red";
break;
}
}
}
}
}
And I have defined my listview in xaml as follows:
<ListView x:Name="lvConnections">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick" />
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Color}" Value="Green">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Red">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Yellow">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Width="150" MaxHeight="50" Grid.Column="0" Grid.Row="0" Orientation="Horizontal" >
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontWeight="ExtraBlack" TextWrapping="Wrap" Padding="10"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The binding does not work and all my listview element have no background color. I do not need exactly the same solution via ListView.Resources binding, but I also have did not succed in other approaches.
If you move the trigger Style from ListView.Resources to StackPanel.Resources (and change the TargetType to StackPanel) then the background colors will display using this approach.
<StackPanel Width="150" MaxHeight="50" Grid.Column="0" Grid.Row="0" Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding Color}" Value="Green">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Red">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Yellow">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontWeight="ExtraBlack" TextWrapping="Wrap" Padding="10"/>
</StackPanel>
You will also need to look at implementing INotifyPropertyChanged on ConnectionItem for the colors to update when Status is changed.
public class ConnectionItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ConnectionItem(string name)
{
Name = name;
}
public string Name { get; }
private string _color = "Red";
public string Color
{
get => _color;
set
{
if (value == _color) return;
_color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));
}
}
private ConnectionStatus _status;
public ConnectionStatus Status
{
get => _status;
set
{
if (value == _status) return;
_status = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Status"));
switch (value)
{
case ConnectionStatus.Connected:
Color = "Yellow";
break;
case ConnectionStatus.Ready:
Color = "Green";
break;
default:
Color = "Red";
break;
}
}
}
}
Note that Status and Color now have both get and set accessors and that
that Status is setting the Color property rather than directly setting the _color field.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lvConnections.ItemsSource = new ObservableCollection<ConnectionItem>()
{
new ConnectionItem("Starts Connected") { Status = ConnectionStatus.Connected, },
new ConnectionItem("Starts Ready") { Status = ConnectionStatus.Ready, },
new ConnectionItem("Starts Default"),
};
}
private void ListViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var item = (sender as ListViewItem)?.DataContext as ConnectionItem;
switch (item.Status)
{
case ConnectionStatus.Connected:
item.Status = ConnectionStatus.Ready;
break;
case ConnectionStatus.Ready:
item.Status = ConnectionStatus.Disconnected;
break;
default:
item.Status = ConnectionStatus.Connected;
break;
}
}
}
You can even go one step further, remove the Color property from ConnectionItem altogether (and the switch setting it in Status) and use Status values in the Style triggers.
ConnectionItem
public class ConnectionItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ConnectionItem(string name)
{
Name = name;
}
public string Name { get; }
private ConnectionStatus _status;
public ConnectionStatus Status
{
get => _status;
set
{
if (value == _status) return;
_status = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
}
Style
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Ready">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Disconnected">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Connected">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
bind the background to the Color property.
<ListView x:Name="lvConnections">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick" />
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding Color}"/>
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Width="150" MaxHeight="50" Grid.Column="0" Grid.Row="0" Orientation="Horizontal" >
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontWeight="ExtraBlack" TextWrapping="Wrap" Padding="10"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and set the Color property to a brush object
public class ConnectionItem
{
public ConnectionItem(string name)
{
Name = name;
}
public string Name { get; }
private Brush _color = Brushes.Red;
public Brush Color { get => _color; }
private ConnectionStatus _status;
public ConnectionStatus Status
{
set
{
if (value == _status)
{
return;
}
else
{
switch (value)
{
case ConnectionStatus.Connected:
_color = Brushes.Yellow;
break;
case ConnectionStatus.Ready:
_color = Brushes.Green;
break;
default:
_color = Brushes.Red;
break;
}
}
}
}
}
Your ListViewItem style doesn't get applied because your have set the ItemContainerStyle property to another Style. You should move your triggers to the ItemContainerStyle:
<ListView x:Name="lvConnections">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick" />
<Style.Triggers>
<DataTrigger Binding="{Binding Color}" Value="Green">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Red">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Color}" Value="Yellow">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Width="150" MaxHeight="50" Grid.Column="0" Grid.Row="0" Orientation="Horizontal" >
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontWeight="ExtraBlack" TextWrapping="Wrap" Padding="10"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

ListViewItem change text color using index

I want to create a playlist.
But I have a problem to highlight the selected item indicated by the user and change the background color or the color of the text item that is played now.
Properties i want to show - what is play now (int IndexToPlay) what user selects (SelectedIndex)
The user should select only one item
I use WPF, MVVM, and I read a lot of posts, but unfortunately I have not found a solution.
<ListView Name="List"
Grid.ColumnSpan="11" Margin="7,0,7,0.4" Grid.Row="9"
ItemsSource="{Binding MusicList, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedIndex}"
>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you have IsSelected and IsPlaying properties in your ViewModel class:
public class MusicItem
{
private bool _isPlaying;
public bool IsPlaying
{
get
{
return _isPlaying;
}
set
{
_isPlaying = value;
OnPropertyChanged();
}
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged();
}
}
}
You can set the ItemContainerStyle to bind the IsSelected property and then you can use a style on the ItemTemplate with a DataTrigger to change the appearance of the ItemTemplate:
<ListView Name="List"
ItemsSource="{Binding MusicList, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedIndex}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPlaying}" Value="True">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

WPF DataGrid different controls in the same column - incorrect binding

I am developing the application which performs checks on the list of items. Each item has the list of the checks that need to be performed on it. Each check can be one of 3 types: CheckBox, ComboBox, TextBox.
I would like to have Datagrid with 2 columns (one for item name, second for list of checks). Second column contains another DataGrid with 2 columns (one for check name, second for check control). The purpose is to have different types of controls in the same column bound with the Check models.
The problem is that binding with CheckValue doesn't work, however bindings with all the other properties work fine.
The last column contains CheckBoxes, TextBox and ComboBox, however they are not filled with any values.
Does anyone know what is wrong with below code?
Here are examples of model classes
public class Item
{
public string ItemName { get; set; }
public ObservableCollection<Check> Checks { get; set; }
public Item()
{
Checks = new ObservableCollection<Check>();
}
}
public enum CheckType
{
CheckBox,
ComboBox,
TextBox
}
public abstract class Check
{
public string CheckName { get; set; }
public CheckType CheckType { get; protected set; }
public abstract object CheckValue { get; set; }
}
public class CheckBox : Check
{
private bool checkValue;
public CheckBox()
{
CheckType = CheckType.CheckBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = (bool)value;
}
}
}
public class ComboBox : Check
{
private List<string> checkValue;
public ComboBox()
{
CheckType = CheckType.ComboBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as List<string>;
}
}
}
public class TextBox : Check
{
private string checkValue;
public TextBox()
{
CheckType = CheckType.TextBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as string;
}
}
}
public class MainViewModel
{
public ObservableCollection<Item> Items { get; set; }
public MainViewModel()
{
Items = new ObservableCollection<Item>();
Item item = new Item();
item.ItemName = "First item";
Check check1 = new CheckBox() { CheckName = "Check 1", CheckValue = true };
Check check2 = new CheckBox() { CheckName = "Check 2", CheckValue = false };
Check text1 = new TextBox() { CheckName = "Check 3", CheckValue = "Please enter check" };
Check combo1 = new ComboBox() { CheckName = "Check 4", CheckValue = new List<string> { "Value1", "Value2" } };
item.Checks.Add(check1);
item.Checks.Add(check2);
item.Checks.Add(text1);
item.Checks.Add(combo1);
Items.Add(item);
}
}
And finally here is XAML code of the main window.
<Window x:Class="ItemTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm ="clr-namespace:ItemTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainViewModel x:Key="mainViewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource mainViewModel}}">
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Binding="{Binding ItemName}" />
<DataGridTemplateColumn Header="Checks">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Checks}" AutoGenerateColumns="False" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CheckName}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding CheckType}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding CheckValue}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Just set the ItemControl's Content property:
<ContentControl Content="{Binding}">
WPF will automatically set DataTemplate's DataContext to its parent ContentControl's Content. But in your XAML you don't set the Content property (you only specify ContentControl's Style, but forget to set its Content).
And don't forget to set UpdateSourceTrigger=PropertyChanged on your control bindings, otherwise you may see no updates in your viewmodel.
XAML example working, with binding for BindingList :
<DataGrid x:Name="dataGridParametros"
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
HeadersVisibility="All"
ItemsSource="{Binding}"
RowHeaderWidth="20"
SelectionUnit="FullRow"
ScrollViewer.CanContentScroll="True"
CanUserAddRows="false"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
FontFamily="Arial"
CellEditEnding="dataGridParametros_CellEditEnding" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding IdParametro}" Header="Id" FontFamily="Arial" IsReadOnly="True" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding Codigo}" Header="Código" FontFamily="Arial" IsReadOnly="True"/>
<DataGridTextColumn Width="200" Binding="{Binding Mnemonico}" Header="Mnemonico" FontFamily="Arial" IsReadOnly="True" />
<DataGridTextColumn Width="250*" Binding="{Binding Descricao}" Header="Descrição" FontFamily="Arial" IsReadOnly="True" />
<DataGridTemplateColumn Header="Valor" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding TipoCampo}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Combobox-Datatemplate & Selection TextBox

I have a combobox which has 2 items. Each item is an objet deriving from a common interface. There is a DisplayText property on each object. Each object is templated so as to have a different visual. Everything works fine, except while selecting one of those objects, the visual is getting displayed in the combobox textbox. I want it to display the SelectedText property of the selected object in the textbox and the DisplayText inside the item template. How do I specify my binding for that please?
Here is my code:
public interface IMyDate
{
string DisplayText { get; }
string SelectedText { get; }
}
public class TodayMinus1 : IMyDate
{
public string DisplayText { get { return "Yesterday"; } }
public string SelectedText{get { return DateTime.Today.AddDays(-1).ToString(); }}
}
public class Today : IMyDate
{
public string DisplayText { get { return "TODAY"; } }
public string SelectedText { get { return DateTime.Today.ToString(); } }
}
public class MyMainViewModel
{
public MyMainViewModel()
{
MyDates = new List<IMyDate>() {new Today(), new TodayMinus1()};
}
public List<IMyDate> MyDates { get; set; }
public IMyDate SelectedDate { get; set; }
}
<ComboBox MaxHeight="26" VerticalAlignment="Center" x:Name="contextDropdown" ItemsSource="{Binding MyDates}" SelectedItem="{Binding SelectedDate}" Grid.Column="1" Width="150" Margin="5">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type local:TodayMinus1}">
<TextBlock Text="{Binding DisplayText}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Today}">
<TextBlock Text="{Binding DisplayText}"/>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
Please note that this is an oversimplified example and I have implemented INPC for all my objects.
Try this:
<ComboBox MaxHeight="26" VerticalAlignment="Center" x:Name="contextDropdown" ItemsSource="{Binding MyDates}"
SelectedItem="{Binding SelectedDate}" Grid.Column="1" Width="150" Margin="5">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type local:TodayMinus1}">
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding DisplayText}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding SelectedText}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Today}">
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding DisplayText}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding SelectedText}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
Added a DataTrigger inside the DataTemplate to achieve your requirement. Try this.
<ComboBox MaxHeight="26" VerticalAlignment="Center" x:Name="contextDropdown" ItemsSource="{Binding MyDates}" SelectedItem="{Binding SelectedDate}" Grid.Column="1" Width="150" Margin="5">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type local:TodayMinus1}">
<TextBlock Text="{Binding DisplayText}" x:Name="DisplayBox"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}" Value="{x:Null}">
<Setter TargetName="DisplayBox" Property="Text" Value="{Binding SelectedText}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Today}">
<TextBlock Text="{Binding DisplayText}" x:Name="DisplayBox"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}" Value="{x:Null}">
<Setter TargetName="DisplayBox" Property="Text" Value="{Binding SelectedText}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>

Categories