Change context menu in WPF TreeView for data - wpf

Is there a way to specify in a TreeView's HierarchicalDataTemplate to use a different ContextMenu depending on a property on the data an item is bound to?
For instance, display one ContextMenu if Item.IsFile is true, display a different one if Item.IsFolder is true, etc.

This is example for ListBox, I think you can easily modify it to work with TreeView.
XAML:
...
<Window.Resources>
<ContextMenu x:Key="FileContextMenu">
...
</ContextMenu>
<ContextMenu x:Key="DirContextMenu">
...
</ContextMenu>
<local:ItemToContextMenuConverter x:Key="ContextMenuConverter" />
</Window.Resources>
...
<ListBox x:Name="SomeList">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name}" ContextMenu="{Binding Converter={StaticResource ContextMenuConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code:
class Item
{
public string Name { get; set; }
public bool IsFile { get; set; }
}
[ValueConversion(typeof(Item), typeof(ContextMenu))]
public class ItemToContextMenuConverter : IValueConverter
{
public static ContextMenu FileContextMenu;
public static ContextMenu DirContextMenu;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Item item = value as Item;
if (item == null) return null;
return item.IsFile ? FileContextMenu : DirContextMenu;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ItemToContextMenuConverter.FileContextMenu
= this.Resources["FileContextMenu"] as ContextMenu;
ItemToContextMenuConverter.DirContextMenu
= this.Resources["DirContextMenu"] as ContextMenu;
List<Item> items = new List<Item>();
items.Add(new Item() { Name = "First", IsFile = true });
items.Add(new Item() { Name = "Second", IsFile = false });
SomeList.ItemsSource = items;
}

Hi I am doing similar thing on TreeView and I don't like that ItemToContextMenuConverter is executed on each item even if it is not used. It's maybe ok in a small project but if you add Enable/Disable code for each MenuItem than it can be slow.
This is maybe not the best (I just started with WPF), but I will share it with you.
Menu Resources:
<Window.Resources>
<ContextMenu x:Key="MnuFolderFavorites" StaysOpen="True">
<MenuItem Header="Remove from Favorites" Click="MnuFolder_RemoveFromFavorites_Click"></MenuItem>
</ContextMenu>
<ContextMenu x:Key="MnuFolder" StaysOpen="True">
<MenuItem Header="New Folder"></MenuItem>
<MenuItem Header="Rename" x:Name="MnuFolderRename" Click="MnuFolder_Rename_Click"></MenuItem>
<MenuItem Header="Add to Favorites" Click="MnuFolder_AddToFavorites_Click"></MenuItem>
</ContextMenu>
</Window.Resources>
TreeView:
<TreeView x:Name="TvFolders">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type data:Folder}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal" PreviewMouseRightButtonDown="TvFoldersStackPanel_PreviewMouseRightButtonDown">
<Image Width="20" Height="20" Source="{Binding ImagePath}" />
<TextBlock Text="{Binding Title}" Margin="5,0,0,0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code:
private void TvFoldersStackPanel_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) {
((StackPanel) sender).ContextMenu = null;
Data.Folder item = (Data.Folder) ((StackPanel) sender).DataContext;
if (!item.Accessible) return;
if (item.Parent != null && item.Parent.Title.Equals("Favorites")) {
((StackPanel) sender).ContextMenu = MainWindow.Resources["MnuFolderFavorites"] as ContextMenu;
} else {
((StackPanel) sender).ContextMenu = MainWindow.Resources["MnuFolder"] as ContextMenu;
foreach (MenuItem menuItem in ((StackPanel) sender).ContextMenu.Items) {
switch (menuItem.Name) {
case "MnuFolderRename": {
menuItem.IsEnabled = item.Parent != null;
break;
}
}
}
}
}
private void MnuFolder_RemoveFromFavorites_Click(object sender, RoutedEventArgs e) {
string path = ((Data.Folder)((MenuItem)sender).DataContext).FullPath;
Settings.Default.FolderFavorites.Remove(path);
Settings.Default.Save();
FavoritesFolder?.AddFavorites(true);
}

Related

Changing a property controller based on other controller's property value in wpf

Consider in my wpf application, I have a checkbox and 2 textedits, as below:
<CheckBox x:Uid="Checkbox_1" FlowDirection="RightToLeft" IsChecked="{Binding TickCheckBox, Mode=TwoWay}" Style="{StaticResource StandardCheckBoxStyle}">My Checkbox</CheckBox>
<dxe:TextEdit x:Uid="dxe:TextEdit_1" Grid.Row="1" Grid.Column="1" Width="100" Style="{StaticResource FleetScheduledHoursStyle}" EditValue="{Binding RealValue, Mode=OneWay}" EditMode="InplaceInactive" ToolTipService.ShowDuration="20000" />
<dxe:TextEdit x:Uid="dxe:TextEdit_2" Grid.Row="1" Grid.Column="1" Width="100" Style="{StaticResource FleetScheduledHoursStyle}" EditValue="{Binding RealValue, Mode=OneWay}" EditMode="InplaceInactive" ToolTipService.ShowDuration="20000" />
The TickCheckBox is bound to a property in my viewmodel as below:
private bool tickCheckBox;
public bool TickCheckBox
{
get
{
return this.tickCheckBox;
}
set
{
if (this.TickCheckBox.Equals(value))
{
return;
}
this.tiketCheckBox = value;
this.NotifyPropertyChange(() => this.TickCheckBox);
}
}
How do I change the property "EditMode" of one of the textedit (say Text_Edit1) to "InplaceActive" when I ticked the checkbox?
Thanks for your help!
You can use an IValueConverter:
BoolToEditModeConverte.cs
public class BoolToEditModeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool isChecked))
{
throw new ArgumentException("Converter value must be of type 'bool'");
}
return isChecked
? EditMode.InplaceInactive
: EditMode.None;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Usage
<Window>
<Window.Resources>
<BoolToEditModeConverte x:Key="BoolToEditModeConverte" />
</Window.Resources>
<CheckBox x:Name="MyCheckbox" />
<TextEdit EditMode="{Binding ElementName=MyCheckBox,
Path=IsChecked,
Converter={StaticResource BoolToEditModeConverte}}" />
</Window>
Since you're using POCOViewModel, you just define a property for the TextEdit.EditMode and binding in the xaml, and define a method for the TickCheckBox changed event in the poco view model, like this:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
this.DataContext = Vm.Create();
}
}
[POCOViewModel]
public class Vm {
public virtual bool TickCheckBox { get; set; } = false;
public virtual EditMode EditMode { get; set; } = EditMode.InplaceInactive;
public static Vm Create() => ViewModelSource.Create(() => new Vm());
protected void OnTickCheckBoxChanged() {
if (this.TickCheckBox) {
// or this.EditMode = EditMode.InplaceActive;
this.EditMode = EditMode.Standalone;
} else {
this.EditMode = EditMode.InplaceInactive;
}
}
}
and the xaml:
<StackPanel>
<CheckBox x:Uid="Checkbox_1" FlowDirection="RightToLeft" IsChecked="{Binding TickCheckBox, Mode=TwoWay}">
My Checkbox
</CheckBox>
<dxe:TextEdit
x:Uid="dxe:TextEdit_1"
EditMode="{Binding EditMode}"
EditValue="{Binding RealValue, Mode=OneWay}"
ToolTipService.ShowDuration="20000" />
<dxe:TextEdit
x:Uid="dxe:TextEdit_2"
EditMode="Standalone"
EditValue="{Binding RealValue, Mode=OneWay}"
ToolTipService.ShowDuration="20000" />
</StackPanel>

WPF, How to change the properties (visibility.. ) of dynamically created UI components on load

I want to change the properties of the dynamically populated UI components.
Here is my sample UI. The label and three textboxes created dynamically.
I want to change the visibility of the third textbox if the label content is R11.
and add a combo box with the static resource if the label is R12
Here is my sample code
my main screen XAML
<StackPanel>
<control:MethodControl></control:MethodControl>
<ContentControl Content="{Binding ChildViewModel}" />
</StackPanel>
MainViewModel
class MethodViewModel : ViewModelBase
{
#region Properties
private Method _method;
private PropertyViewModel _childViewModel;
#endregion
#region Getter & Setters
public PropertyViewModel ChildViewModel
{
get { return this._childViewModel; }
set
{
if (this._childViewModel != value)
{
this._childViewModel = value;
RaisePropertyChanged(() => ChildViewModel);
}
}
}
public Method Method
{
get { return _method; }
}
public ICommand UpdateCommand
{
get; private set;
}
#endregion
#region Constructor
/// <summary>
/// Initialize a new interface of the MEthodViewModel class
/// </summary>
public MethodViewModel()
{
//test
_method = new Method();
PropertyViewModel pwm = new PropertyViewModel();
pwm.CollectProperties(_method.Name, _method.Helper);
ChildViewModel = pwm;
UpdateCommand = new UpdateCommand(SaveChanges, () => string.IsNullOrEmpty(_method.Error));
}
#endregion
#region Functions
public void SaveChanges()
{
PropertyViewModel pwm = new PropertyViewModel();
pwm.CollectProperties(_method.Name, _method.Helper);
ChildViewModel = pwm;
}
Child View Model
class PropertyViewModel : ViewModelBase
{
private ObservableCollection<Property> _properties;
public ObservableCollection<Property> Properties
{
get { return _properties; }
}
public PropertyViewModel(string method, string reflection)
{
_properties = new ObservableCollection<Property>();
CollectProperties(method, reflection);
}
public void CollectProperties(string method, string reflection)
{
_properties.Clear();
int methodindex = Array.IndexOf((String[])Application.Current.Resources["MethodNames"], method);
switch (methodindex)
{
case 0:
foreach (String prop in (String[])Application.Current.Resources["Result1"])
{
PopulateProperty(prop, true);
}
break;
default:
foreach (String prop in (String[])Application.Current.Resources["Result2"])
{
PopulateProperty(prop, true);
}
break;
}
}
public PropertyViewModel()
{
_properties = new ObservableCollection<Property>();
}
private void PopulateProperty(string prop, bool p1)
{
Property temp = new Property(prop, "", 0, "");
_properties.Add(temp);
}
}
ChildViewModel XAML
<StackPanel >
<ItemsControl ItemsSource = "{Binding Properties}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<Label Content="{Binding Name}"></Label>
<TextBox Text = "{Binding Path, Mode=TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding StdDev, Mode=TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding Unit, Mode=TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
and my resources
<x:Array x:Key="MethodNames" Type="sys:String"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>MM1</sys:String>
<sys:String>MM2</sys:String>
<sys:String>MM3</sys:String>
</x:Array>
<x:Array x:Key="HelperMethods" Type="sys:String"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>HM1</sys:String>
<sys:String>HM2</sys:String>
<sys:String>HM3</sys:String>
</x:Array>
<x:Array x:Key="Result1" Type="sys:String"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>R11</sys:String>
<sys:String>R12</sys:String>
<sys:String>R13</sys:String>
</x:Array>
<x:Array x:Key="Result2" Type="sys:String"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>R21</sys:String>
<sys:String>R22</sys:String>
<sys:String>R23</sys:String>
</x:Array>
<DataTemplate DataType="{x:Type modelViews:PropertyViewModel}">
<control:PropertyControl />
</DataTemplate>
part of the UI is changing with the selections of the combo boxes.
I need whole 3 text boxes for some options, only 1 for some and 1 textbox and 1 combo box for some options depends on the label name.
How can I add this property to dynamically populated user control?
You could add one or several triggers to the DataTemplate, e.g.:
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<Label Content="{Binding Name}"></Label>
<TextBox Text = "{Binding Path, Mode=TwoWay}" Width = "100" Margin="3 5 3 5"/>
<TextBox Text="{Binding StdDev, Mode=TwoWay}" Width="100" Margin="3 5 3 5"/>
<TextBox x:Name="third" Text="{Binding Unit, Mode=TwoWay}" Width="100" Margin="3 5 3 5"/>
<ComboBox x:Name="combo" Visibility="Collapsed" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Name}" Value="R11">
<Setter TargetName="third" Property="Visibility" Value="Collapsed" />
<Setter TargetName="combo" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I find an easy solution to my question, so let me add it.
I add visibility in the properties and bind the textbox visibility to it.
here is the bool the visibility converter
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool) value ? Visibility.Visible : Visibility.Collapsed;
}
 
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Do the conversion from visibility to bool
}
}
Resource
<converter:BoolToVisibilityConverter x:Key="converter"></converter:BoolToVisibilityConverter>
text box
<TextBox Visibility="{Binding HasUnit,
Converter={StaticResource converter}}" />
 

ListView DataTemplate binding

I have the following ListView:
<ListView Name="listView">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility"
Value="Collapsed"/>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Margin="0"
VerticalAlignment="Center"
IsChecked="{Binding IsChecked}"
Visibility="{Binding IsChecked, Converter={StaticResource boolToVis}}">
</CheckBox>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="0"
Text="{Binding Text}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The items in the ListView are of the below type:
public class CheckBoxListViewItemSource : INotifyPropertyChanged
{
public CheckBoxListViewItemSource(String text)
{
m_text = text;
}
public bool IsChecked
{
get { return m_checked; }
set
{
if (m_checked == value) return;
m_checked = value;
RaisePropertyChanged("IsChecked");
}
}
public String Text
{
get { return m_text; }
set
{
if (m_text == value) return;
m_text = value;
RaisePropertyChanged("Text");
}
}
public override string ToString()
{
return Text;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propName)
{
PropertyChangedEventHandler eh = PropertyChanged;
if (eh != null)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
private bool m_checked;
private String m_text;
}
The visibility of the checkbox in the ListView is bound to the value of IsChecked of the ListViewItem. The converter is a simple bool to visibility converter:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? Visibility.Visible : Visibility.Collapsed;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
In the code behind of the ListView I have:
void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.RemovedItems)
{
CheckBoxListViewItemSource source = item as CheckBoxListViewItemSource;
source.IsChecked = false;
}
foreach (var item in e.AddedItems)
{
CheckBoxListViewItemSource source = item as CheckBoxListViewItemSource;
source.IsChecked = true;
}
}
The binding for the checkbox visibility is not working for me. The default IsChecked value is false so the list appears with no checkboxes. If I select an item, the checkbox does not appear.
However, if I set the default value of IsChecked to true, all of the list items appear with a checkbox and if I select an item and then deselect it, the checkbox correctly disappears.
What I am trying to achieve is that all of the items start off with no checkbox, selecting an item displays a checked checkbox, and deselecting an item hides the checkbox.
Any ideas where I am going wrong?
Manually set width of the first GridViewColumn to a fixed value. It seems ListView sets width of it to zero if it contains nothing and doesn't update width when checkboxes start to appear.
Alternatively, change code of BoolToVisibilityConverter to return Visibility.Hidden instead of Visibility.Collapsed.
I know this has been answered but I got around this problem using the following:
<GridViewColumn Header="MyColumn">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding MyItem, UpdateSourceTrigger=PropertyChanged}" ContentTemplate="{StaticResource myTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
And in the Window I had a DataTemplate defined for the type that MyItem was:
<Window.Resources>
<DataTemplate DataType="{x:Type myViewModels:MyItemViewModel}" x:Key="myTemplate" >
...template code
</DataTemplate>
</Window.Resources>

How to Bind Flags Enums To ListBox In MVVM

I want to bind an enum which has flags attribute to a listbox with a check list box item template in mvvm pattern? How can I do this?
[Flags]
public enum SportTypes
{
None = 0,
Baseball = 1,
Basketball = 2,
Football = 4,
Handball = 8,
Soccer = 16,
Volleyball = 32
}
<ListBox Name="checkboxList2"
ItemsSource="{Binding Sports}"
Margin="0,5"
SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter}}"
Content="{Binding Item}"/>
</DataTemplate>
</ListBox.ItemTemplate>
You can't easily bind the value directly, because the converter can't build the flag combination from a single flag. So you need to manage a collection of flags, and build the combination based on this collection. The difficulty is that the ListBox.SelectedItems property is readonly. However, this blog post gives a nice workaround, using an attached property.
Here's a complete example using this solution :
Code-behind
[Flags]
public enum MyEnum
{
Foo = 1,
Bar = 2,
Baz = 4
}
public partial class TestEnum : Window, INotifyPropertyChanged
{
public TestEnum()
{
InitializeComponent();
_flags = (MyEnum[])Enum.GetValues(typeof(MyEnum));
_selectedFlags = new ObservableCollection<MyEnum>();
_selectedFlags.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_selectedFlags_CollectionChanged);
this.DataContext = this;
}
void _selectedFlags_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (_selectedFlags.Count == 0)
Value = default(MyEnum);
else
Value = _selectedFlags.Aggregate((v, acc) => acc | v);
}
private MyEnum[] _flags;
public MyEnum[] Flags
{
get { return _flags; }
set
{
_flags = value;
OnPropertyChanged("Flags");
}
}
private ObservableCollection<MyEnum> _selectedFlags;
public ObservableCollection<MyEnum> SelectedFlags
{
get { return _selectedFlags; }
set
{
_selectedFlags = value;
OnPropertyChanged("SelectedFlags");
}
}
private MyEnum _value;
public MyEnum Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
var currentFlags = _flags.Where(f => _value.HasFlag(f));
var addedFlags = currentFlags.Except(_selectedFlags).ToArray();
var removedFlags = _selectedFlags.Except(currentFlags).ToArray();
foreach (var f in addedFlags)
{
_selectedFlags.Add(f);
}
foreach (var f in removedFlags)
{
_selectedFlags.Remove(f);
}
}
}
private DelegateCommand<MyEnum> _setValueCommand;
public ICommand SetValueCommand
{
get
{
if (_setValueCommand == null)
{
_setValueCommand = new DelegateCommand<MyEnum>(v => Value = v);
}
return _setValueCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<Window x:Class="TestPad.TestEnum"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:TestPad"
Title="TestEnum" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Value}" />
<ListBox Grid.Row="1"
ItemsSource="{Binding Flags}"
SelectionMode="Extended"
my:MultiSelectorBehavior.SynchronizedSelectedItems="{Binding SelectedFlags}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ItemsControl Grid.Row="2"
ItemsSource="{Binding Flags}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.SetValueCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding}"
Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
NOTE: for the sake of simplicity, the ViewModel is the Window class. In a real MVVM application, you would of course use a separate ViewModel class...
I see two solutions: One that is fully dynamic, and one that is static. The dynamic solution is a lot of work and IMO not trivial. The static one should be easy:
Create an Panel within your DataTemplate. There in, place for each Flag-Value a CheckBox. Then use the ConverterParameter to specifiy the flag, the converter should use. This would look something like this:
<StackPanel>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Baseball}}" Content="Baseball"/>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Basketball}}" Content="Basketball"/>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Football}}" Content="Football"/>
<CheckBox IsChecked="{Binding ..../>
</StackPanel/>
In your converter you only have to do some logical AND-comparisons and you will have what you're looking for. If you're interested in the dynamic solution, make a comment, I can give you some ideas where to start. But IMO this will really not be trivial.
Additonal info
If you want to have a list instead of the StackPanel, use a ScrollViewer in a Border or even a ListBox.
To expand on Chris's post, here's a more thorough explanation of how you can do this.
This isn't the most ideal scenario, as the property holding the enum has to be a bit more complex than usual, but it works.
Converter code:
public class EnumFlagConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var theEnum = value as Enum;
return theEnum.HasFlag(parameter as Enum);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var theEnum = parameter as Enum;
return theEnum;
}
}
XAML for converter:
<StackPanel>
<StackPanel.Resources>
<local:EnumFlagConverter x:Key="MyConverter" />
</StackPanel.Resources>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Baseball}}" Content="Baseball"/>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Basketball}}" Content="Basketball"/>
<CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Football}}" Content="Football"/>
<CheckBox IsChecked="{Binding ..../>
</StackPanel>
Then, to bind to this, I did a bit of trickery to get the converter to work properly:
private SportTypeEnum _TheSportType;
public SportTypeEnum _TheSportType
{
get { return _TheSportType; }
set
{
if (_TheSportType.HasFlag(value))
_TheSportType &= ~value;
else
_TheSportType |= value;
NotifyPropertyChanged();
}
}
Because of this special setter logic, you probably want to include a method like this to allow you to fully set the value from code:
private void ResetTheSportType()
{
_TheSportType = _TheSportType.None;
NotifyPropertyChanged(() => TheSportType);
}

how can we access the controls in the Itemtemplate Silverlight

i have a list box. i have an item template with a stack panel in it.
it has a text box and a check box in the item template.
is there a way i can access the check box and enable/disable it on selected index changed?
<UserControl.Resources>
<DataTemplate x:Key="UserApplicationsTemplate">
<StackPanel Orientation="Horizontal"
Margin="2"
ToolTipService.Placement="Mouse"
ToolTipService.ToolTip="{Binding Description, Mode=OneWay}">
<TextBlock Text="{Binding Mode=OneWay}"
TextWrapping="Wrap"
Width="100"
DataContext="{Binding ApplicationName, Mode=OneWay}" />
<CheckBox x:Name="{Binding ApplicationName, Mode=OneWay}"
Margin="5,0,0,0"
Click="IsActive_Clicked"
IsChecked="{Binding IsActive, Mode=OneWay}"
Content="IsActive"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<ListBox x:Name="lstbxUserApplications"
Height="357"
ItemsSource="{Binding Mode=OneWay}"
SelectionMode="Single"
ItemTemplate="{StaticResource UserApplicationsTemplate}" />
Assming you have your ItemsSource binded:
<ListBox x:Name="myList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Check, Mode=TwoWay}" />
<TextBlock Text="{Binding Name, Mode=TwoWay}"
Width="100" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="button1"
Content="Uncheck 2"
Click="button1_Click" />
You don't actually need to change the CheckBox.IsChecked property, but the value on your ItemsSource:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
myList.ItemsSource = ListInfo.getInfo();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
ListInfo item = myList.Items[1] as ListInfo;
item.Check = !item.Check;
}
}
public class ListInfo : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
NotifyPropertyChange("Name");
}
}
private bool check;
public bool Check
{
get
{
return check;
}
set
{
check = value;
NotifyPropertyChange("Check");
}
}
public static ObservableCollection<ListInfo> getInfo()
{
ObservableCollection<ListInfo> data = new ObservableCollection<ListInfo>();
data.Add(new ListInfo { Name = "Some text 1", Check = true });
data.Add(new ListInfo { Name = "Some text 2", Check = false });
data.Add(new ListInfo { Name = "Some text 3", Check = true });
return data;
}
public void NotifyPropertyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
If you take a look at the event handler for the Click event on the button, you can see that all I did was get the item and changed the value. This immediately reflects on the UI.
UPDATE: I see that this was not what you asked for. Here are a couple of ideas:
You can have an event handler for your check box:
<ListBox x:Name="myList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Check, Mode=TwoWay}"
IsEnabled="True"
Content="{Binding Name, Mode=TwoWay}"
Click="CheckBox_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And get the reference on the code behind:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox chk = sender as CheckBox;
chk.IsEnabled = false;
}
Of course the problem here is that if you disable the checkbox you lose access to the Click envent.
Another choice is to use the VisualTreeHelper to get reference to your CheckBox when the selection change on the ListBox:
private void myList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox list = sender as ListBox;
ListInfo current = list.SelectedItem as ListInfo;
List<CheckBox> checkBoxes = new List<CheckBox>();
getCheckBoxes(list, ref checkBoxes);
foreach (CheckBox checkBox in checkBoxes)
{
if (checkBox.Content.ToString() == current.Name)
{
checkBox.Foreground = new SolidColorBrush(Colors.Red);
}
}
}
public void getCheckBoxes(UIElement parent, ref List<CheckBox> items)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (child.GetType() == typeof(CheckBox))
{
items.Add(child as CheckBox);
}
getCheckBoxes(child, ref items);
}
}
}
This of course is not the best option for performance but you get more flexibility.
Here is a solution using RadioButtons:
http://leeontech.wordpress.com/2009/03/18/creating-radiobuttonlist/
It should be easy enough to change it to checkboxes.

Resources