I am trying to create a ContextMenu on a DataGrid with multiple Command Bindings. Some have to be bound to the local ViewModel (i.e. the ViewModel of the Row) and some to the parents ViewModel.
I had no luck so far following other solutions. I only get the SubCommand to execute.
MainViewModel
public class MainViewModel : ViewModelBase
{
public ObservableCollection<SubViewModel> Items { get; private set; }
public MainViewModel()
{
Items = new ObservableCollection<SubViewModel>();
Items.Add(new SubViewModel());
Items.Add(new SubViewModel());
}
private RelayCommand _mainCommand;
public ICommand MainCommand
{
get
{
if (_mainCommand == null)
{
_mainCommand = new RelayCommand(
() =>
{
Debug.WriteLine("MAINCOMMAND EXECUTED");
}
);
}
return _mainCommand;
}
}
}
SubViewModel
public class SubViewModel : ViewModelBase
{
private RelayCommand _subCommand;
public ICommand SubCommand
{
get
{
if (_subCommand == null)
{
_subCommand = new RelayCommand(
() =>
{
Debug.WriteLine("SUBCOMMAND EXECTUED");
}
);
}
return _subCommand;
}
}
}
MainWindow
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
DataContext="{Binding Source={StaticResource Locator},
Path=Main}">
<Grid>
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Command="{Binding DataContext.MainCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}}" Header="Main" />
<MenuItem Command="{Binding SubCommand}" Header="Sub" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
</Window>
I have found a way, but I don't think it is too elegant, maybe there is a better way, but this will work:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
DataContext="{Binding Source={StaticResource Locator},
Path=Main}">
<Grid x:Name="Grid">
<DataGrid x:Name="DataGrid" ItemsSource="{Binding Items}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding Path=PlacementTarget.Tag.MainCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}}" Header="Main" />
<MenuItem Command="{Binding SubCommand}" Header="Sub" />
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
</Window>
Related
I have a XamGrid with two columns, Name and Type. Depending on Type, I want to have a different kind of column for Name, thus I'm using a TemplateColumn. In the data template I have a ContentControl with a default ContentTemplate and a DataTrigger that sets the ContentTemplate to a different column style if Type is a specific value.
I am setting alll four templates (ItemTemplate, EditorTemplate, AddNewRowItemTemplate, AddNewRowEditorTemplate) of the TemplateColumn to this data template.
ItemTemplate, AddNewRowItemTemplate and AddNewRowEditorTemplate work as intended, however EditorTemplate does not, see attached pictures:
Here is my code:
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ig="http://schemas.infragistics.com/xaml"
Width="640" Height="480" >
<Window.Resources>
<DataTemplate x:Key="EditorTemplate">
<TextBox Width="64"/>
</DataTemplate>
<DataTemplate x:Key="BoolEditorTemplate">
<CheckBox/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource EditorTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="bool">
<Setter Property="ContentTemplate" Value="{StaticResource BoolEditorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</Window.Resources>
<ig:XamGrid ItemsSource="{Binding DataCollection, RelativeSource={RelativeSource AncestorType=Window}}"
AutoGenerateColumns="False">
<ig:XamGrid.EditingSettings>
<ig:EditingSettings AllowEditing="Row" />
</ig:XamGrid.EditingSettings>
<ig:XamGrid.AddNewRowSettings>
<ig:AddNewRowSettings AllowAddNewRow="Top" />
</ig:XamGrid.AddNewRowSettings>
<ig:XamGrid.Columns>
<ig:TemplateColumn Key="Name"
ItemTemplate="{StaticResource DataTemplate}"
AddNewRowItemTemplate="{StaticResource DataTemplate}"
EditorTemplate="{StaticResource DataTemplate}"
AddNewRowEditorTemplate="{StaticResource DataTemplate}"/>
<ig:TextColumn Key="Type"/>
</ig:XamGrid.Columns>
</ig:XamGrid>
</Window>
MainWindow.xaml.cs:
using System.Collections.ObjectModel;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
public ObservableCollection<Data> DataCollection { get; } = new ObservableCollection<Data>
{
new Data { Name = "Foo", Type = "bool" },
new Data { Name = "Bar", Type = "enum" }
};
}
public class Data
{
public string Name { get; set; }
public string Type { get; set; }
}
}
As explained here on the infragistics forum, for this use case not only is an EditorTemplate needed, but also an EditorStyle.
<Style x:Key="EditorStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource EditorTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="bool">
<Setter Property="ContentTemplate" Value="{StaticResource BoolEditorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="DataTemplate">
<ContentControl Content="{Binding }"
Style="{StaticResource EditorStyle}" />>
</DataTemplate>
[...]
<ig:TemplateColumn Key="Name"
ItemTemplate="{StaticResource DataTemplate}"
AddNewRowItemTemplate="{StaticResource DataTemplate}"
EditorTemplate="{StaticResource DataTemplate}"
AddNewRowEditorTemplate="{StaticResource DataTemplate}"
EditorStyle="{StaticResource EditorStyle}" />
I'm styling TabItems of a TabControl. The problem is, the style affects items of nested TabControls. Propagating styles are the only way I know to get to the TabItems. Anyone know how to style the TabItems on just the outer TabControl?
In my case, the inner tabs are defined in plugins, so I can't access them to try this answer.
Demo app
Here's a demo app of my situation.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl ItemsSource="{Binding}">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" Background="Red">
<ContentPresenter ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding TabConent}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public class TabData
{
public string TabName { get; set; }
public Label TabConent
{
get
{
// In real case, this TabControl from someone else's plugin
var content = new TabControl();
content.Items.Add(new TabItem() { Header = "Nested Tab Item" });
return new Label() { Content = content };
}
}
}
public MainWindow()
{
DataContext = new ObservableCollection<TabData>() { new TabData() { TabName = "Tab Item" } }; ;
InitializeComponent();
}
}
}
Use this, and tell if this is what you want :
<TabControl.Resources>
<Style TargetType="TabItem">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TabControl, AncestorLevel=2}}" Value="{x:Null}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" Background="Red">
<ContentPresenter ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
How can I change a Button template dynamically?
I have a ComboBox where by changing his selected value I want to change a Button Template.
This is what I have been trying to do:
<Window.Resources>
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="#FF2D2D7A" Margin="7.5,9.5,8.5,11" Stroke="Black"
RadiusX="45" RadiusY="45" StrokeThickness="6"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate2" TargetType="{x:Type Button}">
<Grid>
<ed:RegularPolygon Fill="#FFE7F9C9" Height="Auto" InnerRadius="0.47211"
Margin="20.5,16,15.5,8" PointCount="5" Stretch="Fill"
Stroke="Black" StrokeThickness="6" Width="Auto"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ComboBox Name="GroupBoxHeaderComboBox" ItemsSource="{Binding Path=collection}"
DisplayMemberPath="Key" Height="52" Margin="211.5,60,230.5,0"
VerticalAlignment="Top" SelectedIndex="1"/>
<Button Content="Button" HorizontalAlignment="Left" Height="102" Margin="47.5,0,0,91"
VerticalAlignment="Bottom" Width="132"
Template="{DynamicResource ButtonControlTemplate2}"/>
<Button Content="Button" HorizontalAlignment="Right" Height="112.5" Margin="0,0,27.5,85"
VerticalAlignment="Bottom" Width="153"
Template="{DynamicResource ButtonControlTemplate1}"/>
<Button Content="Button" Height="102" Margin="239.5,0,252.5,13.5"
VerticalAlignment="Bottom"
Template="{Binding ElementName=GroupBoxHeaderComboBox, Path=SelectedItem.Value}"/>
</Grid>
And here are the associated Templates:
<Window.Resources>
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="#FF2D2D7A" Margin="7.5,9.5,8.5,11" Stroke="Black"
RadiusX="45" RadiusY="45" StrokeThickness="6"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate2" TargetType="{x:Type Button}">
<Grid>
<ed:RegularPolygon Fill="#FFE7F9C9" Height="Auto" InnerRadius="0.47211"
Margin="20.5,16,15.5,8" PointCount="5" Stretch="Fill"
Stroke="Black" StrokeThickness="6" Width="Auto"/>
</Grid>
</ControlTemplate>
</Window.Resources>
And the code behind:
public partial class MainWindow : Window
{
public Dictionary<string, string> collection
{
get;
private set;
}
public MainWindow()
{
this.InitializeComponent();
DataContext = this;
collection = new Dictionary<string, string>()
{
{ "DynamicResource ButtonControlTemplate2", "{DynamicResource ButtonControlTemplate2}"},
{ "DynamicResource ButtonControlTemplate1", "{DynamicResource ButtonControlTemplate2}"},
};
// Insert code required on object creation below this point.
}
}
Is there another genric way to acomplish this?... I want that most of the code would be xaml.
EDIT:
Is there a point to do it using a style? Let's say I want more then one object to act, otherwise is there a point to change the style and to do it all from there?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public Dictionary<string, ControlTemplate> collection
{
get
{
Dictionary<string, ControlTemplate> controlTemplates = new Dictionary<string, ControlTemplate>();
controlTemplates.Add("ButtonControlTemplate1", FindResource("ButtonControlTemplate1") as ControlTemplate);
controlTemplates.Add("ButtonControlTemplate2", FindResource("ButtonControlTemplate2") as ControlTemplate);
return controlTemplates;
}
}
}
Create a ControlTemplate in Windows resource,
<Window.Resources>
<ControlTemplate x:Key="GreenTemplate" TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="Green"/>
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
Now in run time you can change the template property of button.
private void Button_Clicked(object sender, RoutedEventArgs e)
{
Button btn = e.OriginalSource as Button;
if (btn != null)
{
btn.Template = FindResource("GreenTemplate") as ControlTemplate;
}
}
You can use a data trigger and do it all in xaml.
This uses a tree but the concept is the same
<Window x:Class="WpfBindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfBindingTest"
Title="Window3" Height="300" Width="300" Name="win3" >
<Window.Resources>
<XmlDataProvider x:Key="treeData" XPath="*">
<x:XData>
<Items Name="Items" xmlns="">
<Item1/>
<Item2>
<Item22/>
<Item12/>
<Item13>
<Item131/>
<Item131/>
</Item13>
</Item2>
</Items>
</x:XData>
</XmlDataProvider>
<HierarchicalDataTemplate ItemsSource="{Binding XPath=child::*}" x:Key="template">
<TextBlock Name="textBlock" Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView ItemTemplate="{StaticResource template}"
Name="treeView"
ItemsSource="{Binding Source={StaticResource treeData}}">
<TreeView.ItemContainerStyle>
<!--Using style setter to set the TreeViewItem.IsExpanded property to true, this will be applied
to all TreeViweItems when they are generated-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<Button Width="120" Height="30">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="Default" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=treeView, Path=SelectedItem.Name}" Value="Item12">
<Setter Property="Content" Value="Now changed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
from here.
(I just googled to get an example faster)
Everything in the code below works except for the binding on the ContextMenu. This is evidently due to the fact that the ContextMenu is located inside of a Style, which puts it in a different namescope from the rest of the xaml. I am looking for a solution where I won't have to instantiate a ContextMenu in the code-behind, since the application where I have to apply the solution contains a very large ContextMenu with a lot of bindings. There must be a way to accomplish this in xaml, otherwise it would seem like a serious oversight. Also note that I've already tried traversing the element tree using VisualTreeHelper and LogicalTreeHelper, but I wasn't able to find the ContextMenu from the root element of the Window (these classes evidently skipped over the interesting elements). Anyway, all of the code is below. This can be pasted into a new WPF application in Visual Studio, and nothing is missing.
Here's the code for App.xaml.cs (the xaml was left unchanged):
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
WindowV windowV = new WindowV();
WindowVM windowVM = new WindowVM();
windowV.DataContext = windowVM;
windowV.Show();
}
}
}
Here's the xaml for what was originally Window1:
<Window x:Class="WpfApplication1.WindowV"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication1"
Name="MainWindow"
Title="WindowV" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type ItemsControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLocked}" Value="true">
<Setter Property="ItemsSource" Value="{Binding LockedList}" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsLocked}" Value="false">
<Setter Property="ItemsSource" Value="{Binding RegularList}" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
<MenuItem Header="{Binding MenuItem2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
<MenuItem Header="{Binding MenuItem3, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" />
<Button Name="ToggleButton"
Grid.Row="1"
Content="Toggle Lock"
Click="OnToggleLock" />
</Grid>
</Window>
Here's the codebehind for what was originally Window1:
using System.Windows;
using System.Windows.Markup;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class WindowV : Window
{
public WindowV()
{
InitializeComponent();
}
private void OnToggleLock(object sender, RoutedEventArgs e)
{
if (((WindowVM)(DataContext)).IsLocked == true)
((WindowVM)(DataContext)).IsLocked = false;
else
((WindowVM)(DataContext)).IsLocked = true;
}
}
}
A new class was added to the project called WindowVM. Here's its code:
using System.Collections.Generic;
using System.ComponentModel;
namespace WpfApplication1
{
public class WindowVM : INotifyPropertyChanged
{
public string MenuItem1
{
get
{
string str = "Menu item 1";
return str;
}
}
public string MenuItem2
{
get
{
string str = "Menu item 2";
return str;
}
}
public string MenuItem3
{
get
{
string str = "Menu item 3";
return str;
}
}
public List<string> LockedList
{
get
{
List<string> list = new List<string>();
list.Add("This items control is currently locked.");
return list;
}
}
public List<string> RegularList
{
get
{
List<string> list = new List<string>();
list.Add("Item number 1.");
list.Add("Item number 2.");
list.Add("Item number 3.");
return list;
}
}
private bool _isLocked;
public bool IsLocked
{
get { return _isLocked; }
set
{
if (_isLocked != value)
{
_isLocked = value;
OnPropertyChanged("IsLocked");
}
}
}
public WindowVM()
{
IsLocked = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
Any insight would be very appreciated. Thanks much!
Andrew
Okay - I am having a little trouble following what you are trying to accomplish - but try the following:
<ContextMenu>
<MenuItem Header="{Binding DataContext.MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
...
And to make your code easier, you can then try:
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<MenuItem Header="{Binding MenuItem1}"/>
...
Problem was that you were binding to the UI element Window which does not have a property called MenuItem1 etc. The DataContext property has the data you are interested in.
Alright, this solution works: I changed the WindowV.xaml and WindowV.xaml.cs files as follows. The following corrections fix the problem concerning namescope in xaml.
Here's the new WindowV.xaml file:
<Window x:Class="WpfApplication1.WindowV"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication1"
Name="RootElement"
Title="WindowV" Height="300" Width="300">
<Window.Resources>
<ContextMenu x:Key="myContextMenu" DataContext="{Binding Path=DataContext, ElementName=RootElement}">
<MenuItem Header="{Binding MenuItem1}" />
<MenuItem Header="{Binding MenuItem2}" />
<MenuItem Header="{Binding MenuItem3}" />
</ContextMenu>
<Style TargetType="{x:Type ItemsControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLocked}" Value="true">
<Setter Property="ItemsSource" Value="{Binding LockedList}" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsLocked}" Value="false">
<Setter Property="ItemsSource" Value="{Binding RegularList}" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" ContextMenu="{StaticResource myContextMenu}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" />
<Button Name="ToggleButton"
Grid.Row="1"
Content="Toggle Lock"
Click="OnToggleLock" />
</Grid>
</Window>
Here's the corresponding code-behind:
using System.Windows;
using System.Windows.Markup;
using System;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class WindowV : Window
{
public WindowV()
{
InitializeComponent();
System.Windows.Controls.ContextMenu contextMenu =
FindResource("myContextMenu") as System.Windows.Controls.ContextMenu;
NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this as DependencyObject));
contextMenu.RegisterName("RootElement", this);
}
private void OnToggleLock(object sender, RoutedEventArgs e)
{
if (((WindowVM)(DataContext)).IsLocked == true)
((WindowVM)(DataContext)).IsLocked = false;
else
((WindowVM)(DataContext)).IsLocked = true;
}
}
}
Andrew
I have a enum let's say
enum MyEnum
{
FirstImage,
SecondImage,
ThirdImage,
FourthImage
};
I have binded this Enum to my combobox in XAML.
While defining an combobox I have defined an ItemTemplate of combox to take Two UI element:
TextBlock that show the enum value (Description)
Image
I have done this much in XAML.
I am wondering where I can specify the Image corrosponding to each item of Enum value in a combobox? Is that possible through data trigger ?
I really appreciate if anyone have the XAML for this scenario.
Many Thanks in advance
You can use a DataTrigger, but would be more maintainable if you used a Converter. Here's a sample that uses a DataTrigger for a view of the image and text by itself, and then the same DataTrigger to display the image and text in ListBox and ComboBox, and finally, a ListBox and ComboBox that use a Converter to display the image and text:
XAML
<Window x:Class="WpfSandbox.EnumToImage.EnumToImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfSandbox.EnumToImage"
Title="Enum To Image" SizeToContent="WidthAndHeight" >
<Window.DataContext>
<local:ImageViewModel x:Name="Model" />
</Window.DataContext>
<Window.Resources>
<ObjectDataProvider x:Key="EnumDataProvider"
MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:Decade"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<local:DecadeEnumImageConverter x:Key="ImageConverter" />
<ControlTemplate x:Key="ImageTemplate" >
<StackPanel Orientation="Horizontal">
<Image x:Name="MyImage" Width="64" Height="32" />
<TextBlock Text="{Binding}" VerticalAlignment="Center" />
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="Ninties" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/90s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Eighties" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/80s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Seventies" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/70s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Sixties" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/60s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Fifties" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/50s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Forties" >
<DataTrigger.Setters>
<Setter TargetName="MyImage"
Property="Source"
Value="/EnumToImage/images/40s.jpg"/>
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<DataTemplate x:Key="ItemsTemplateWithConverter">
<StackPanel Orientation="Horizontal">
<Image Width="64" Height="32"
Source="{Binding Converter={StaticResource ImageConverter}}"/>
<TextBlock Text="{Binding}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemsTemplateWithDataTrigger">
<ContentControl Template="{StaticResource ImageTemplate}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl Margin="10" MouseUp="OnImageMouseUp"
HorizontalAlignment="Center" Cursor="Hand"
DataContext="{Binding Path=ImageEnum}"
Template="{StaticResource ImageTemplate}" />
<StackPanel Orientation="Horizontal">
<StackPanel>
<ListView Margin="10"
ItemsSource="{Binding Source={StaticResource EnumDataProvider}}"
ItemTemplate="{StaticResource ItemsTemplateWithConverter}" />
<ComboBox Margin="10"
ItemsSource="{Binding Source={StaticResource EnumDataProvider}}"
ItemTemplate="{StaticResource ItemsTemplateWithConverter}" />
</StackPanel>
<StackPanel>
<ListView Margin="10"
ItemsSource="{Binding Source={StaticResource EnumDataProvider}}"
ItemTemplate="{StaticResource ItemsTemplateWithDataTrigger}" />
<ComboBox Margin="10"
ItemsSource="{Binding Source={StaticResource EnumDataProvider}}"
ItemTemplate="{StaticResource ItemsTemplateWithDataTrigger}" />
</StackPanel>
</StackPanel>
</StackPanel>
</Window>
Code Behind
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Data;
namespace WpfSandbox.EnumToImage
{
/// <summary>
/// Interaction logic for EnumToImage.xaml
/// </summary>
public partial class EnumToImage : Window
{
public EnumToImage()
{
InitializeComponent();
}
private int i = 1;
private void OnImageMouseUp( object sender, MouseButtonEventArgs e )
{
i++;
Model.ImageEnum = ( Decade )i;
if( i == 6 )
i = 0;
}
}
public enum Decade
{
Ninties = 1,
Eighties = 2,
Seventies = 3,
Sixties = 4,
Fifties = 5,
Forties = 6,
};
public class ImageViewModel : INotifyPropertyChanged
{
private Decade _imageEnum;
public Decade ImageEnum
{
get { return _imageEnum; }
set
{
_imageEnum = value;
RaisePropertyChanged( "ImageEnum" );
}
}
public ImageViewModel()
{
ImageEnum = Decade.Ninties;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged( string propertyName )
{
var handler = PropertyChanged;
if( handler != null )
{
handler( this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
public class DecadeEnumImageConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
var myEnum = ( Decade )Enum.Parse( typeof( Decade ), value.ToString() );
switch( myEnum )
{
case Decade.Ninties:
return "/EnumToImage/images/90s.jpg";
case Decade.Eighties:
return "/EnumToImage/images/80s.jpg";
case Decade.Seventies:
return "/EnumToImage/images/70s.jpg";
case Decade.Sixties:
return "/EnumToImage/images/60s.jpg";
case Decade.Fifties:
return "/EnumToImage/images/50s.jpg";
case Decade.Forties:
return "/EnumToImage/images/40s.jpg";
default:
throw new ArgumentOutOfRangeException();
}
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
}
}