Combobox-Datatemplate & Selection TextBox - c#

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>

Related

How to bind property of dynamically created components of a user control to another component?

I have a user control which creates a set of radio buttons based on a list. The radio buttons are created using data template.
<UserControl.Resources>
<SelectableItem:SelectableItem x:Key="vm"></SelectableItem:SelectableItem>
<src:RadioButtonCheckedConverter x:Key="RadioButtonCheckedConverter" />
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=ItemDescription}"
x:Key="ListingDataView" />
<DataTemplate x:Key="GroupingHeaderTemplate">
<TextBlock Text="{Binding Path=Name}" Style="{StaticResource GroupHeaderStyle}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ItemsControl Name="RadioGroup" AutomationProperties.Name="RadioGroup" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
GroupName="{Binding Path=ItemType}"
Content="{Binding Path=ItemDescription}"
FlowDirection="RightToLeft"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Style="{DynamicResource CustomRadioButton}" Margin="20,0" Checked="RadioButton_Checked" Tag="{Binding Path=ItemDescription, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
I use this in a window where I need to change the visibility of a component (stack panel) based on the selected radio button.
<uc:CommonRadioButtonGroup x:Name="SelectionButtonsGroup" ></uc:CommonRadioButtonGroup>
I am trying to change the visibility using style triggers.
<Style x:Key="spStyle" TargetType="StackPanel" >
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static Local:EngineModes.PresentMode}}" Value="Stop">
<Setter Property="StackPanel.Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static Local:EngineModes.PresentMode}}" Value="Wait">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static Local:EngineModes.PresentMode}}" Value="Go">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
I cannot figure out a way to implement the viewmodel for this one. I tried this one:
public class EngineModes : INotifyPropertyChanged
{
public static List<SelectableItem> Modes { get; set; } = new List<SelectableItem>();
public static string PresentMode { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyInfo)
{
App.Current.Dispatcher.BeginInvoke((Action)(() =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyInfo));
}
));
}
}
where "Modes" are the options of the radio button. But it simply does not work.
Ultimately, on selecting a mode using radio button, the visibility of the stack panel must be modified.
Please comment on the correctness of the code.
Edit:
Here is the ItemSource for the user control added in codebehind:
SelectionButtonsGroup.RadioGroup.ItemsSource = EngineModes.Modes;
this.DataContext = EngineModes.PresentMode;
I updated the style as
<Style x:Key="sprecStyle" TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource engineModes}, Path=PresentMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Stop">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource engineModes}, Path=PresentMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Wait">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Source={StaticResource engineModes}, Path=PresentMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Go">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
And added a separate notified event for static property change:
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged
= delegate { };
public static void NotifyStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
and called it where the engine mode is updated.
EngineModes.NotifyStaticPropertyChanged("PresentMode");
And voila! It worked.

WPF DataTrigger not firing on NotifyProperyChanged?

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}

Why changing DataGrid ComboBox value does not update the bound property at all?

I have DataGridComboBoxColumn that is supposed to show integers or text "Default". When I add row the combobox gets correct value from viewmodel's bound property, but when I change value in user interface, the property's set is not called. I tried both SelectedValueBinding and SelectedItemBinding. Converter's ConvertBack is never called. I don't event know should it be called.
Things that work:
DataGrid SelectedItem binding
Text column binding both ways (omitted here for shortness)
Here is my code:
XAML:
<DataGrid Name="SelectionSetsGrid" CanUserAddRows="False" CanUserResizeColumns="True" CanUserSortColumns="True"
ItemsSource="{Binding SelectionSets}" AutoGenerateColumns="False"
SelectedItem="{Binding SelectedSelectionSet}">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Width" SelectedValueBinding="{Binding LineWidthIndex}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel (ViewModel implements INotifyPropertyChanged and SetValue raises PropertyChanged):
public class SelectedObjectsViewModel : ViewModel
{
private int[] _lineWidths = { -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
public ObservableCollection<int> LineWidths { get; private set; };
private ObservableCollection<SelectionSetViewModel> _selectionSets;
public ObservableCollection<SelectionSetViewModel> SelectionSets
{
get { return _selectionSets; }
set { this.SetValue(ref _selectionSets, value); }
}
private SelectionSetViewModel _selectedSelectionSet;
public SelectionSetViewModel SelectedSelectionSet
{
get { return this._selectedSelectionSet; }
set { this.SetValue(ref _selectedSelectionSet, value); }
}
}
ViewModel for DataGrid row (ViewModel implements INotifyPropertyChanged and SetValue raises PropertyChanged):
public class SelectionSetViewModel : ViewModel
{
public SelectionSetViewModel()
{
LineWidthIndex = -1;
}
private int _lineWidthIndex;
public int LineWidthIndex
{
get { return _lineWidthIndex; }
set { SetValue(ref _lineWidthIndex, value); }
}
Converter:
public class IntToIntTextOrDefaultConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if ((int)value == -1)
return Fusion.App.Current.Resources["StrDefault"].ToString();
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
It seems that on some occasions like after editing the text column and pressing enter or adding new row the property WAS actually updated (set called) after changing combobox value. So I just added UpdateSourceTrigger=PropertyChanged to binding and the update to source property happened immediately (and not after some random operation). Note that changing focus from ComboBox was not enough to update source property so I thought it was never updated.
<DataGrid Name="SelectionSetsGrid" CanUserAddRows="False" CanUserResizeColumns="True" CanUserSortColumns="True"
ItemsSource="{Binding SelectionSets}" AutoGenerateColumns="False"
SelectedItem="{Binding SelectedSelectionSet}">
<DataGridComboBoxColumn Header="{StaticResource XpStrTopologyWidth}" SelectedItemBinding="{Binding LineWidthIndex, UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox" BasedOn="{StaticResource Theme.ComboBox.Style}">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.LineWidths}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Converter={StaticResource IntToIntTextOrDefaultConverter}}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>

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>

Listbox DataTemplate clear textbox value on selection

For my ListBox I have a DataTemplate:
<DataTemplate x:Key="lbTemplate" DataType="{x:Type ListBoxItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Naam, Mode=OneWay}" VerticalAlignment="Center" />
<TextBox TextAlignment="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}},
Path=IsSelected, Converter={StaticResource BoolToAlignment}}"
Text="{Binding Path=Aantal, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, TargetNullValue=0}"
</Grid>
</DataTemplate>
Which the properties are bound to:
public class menuItem
{
public int? Aantal { get; set; }
public string Naam { get; set; }
}
How can I clear the TextBox value if the corresponding ListBoxItem is selected without losing my binding to the Aantal? property?
Use Style with DataTrigger:
<Style TargetType="TextBox" x:Key="tbStyle">
<Setter Property="Text" >
<Setter.Value>
<Binding Path="Aantal" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}},
Path=IsSelected}" Value="True">
<Setter Property="Text" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
EDIT
To reset (clear) the bound Aantal property after selection of ListBoxItem:
Add IsSelected flag to your item class, bind it to ListBoxItem.IsSelected property by adding the following style to your ListBox:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>
Then in IsSelected setter, add code: if (value) { this.Aantal = ""; }

Categories