How can I change the TreeView Icon into a folder icon? - wpf

I'm trying to change the icon of my TreeView in a folder icon. Also when it collapses it needs to have an opened folder icon.
My treeview has databound items in it and the code is:
<TreeView x:Name="TreeViewCategories" Grid.Row="0" Grid.Column="1" Height="610" HorizontalAlignment="Left" Margin="29,111,0,0" VerticalAlignment="Top" Width="315" BorderThickness="0" Background="Transparent" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock FontSize="20" Text="{Binding Name}" PreviewMouseDown="TextBlock_PreviewMouseDown"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Also this is how I fill the treeview with items from XML (It's a snipped out of alot of code:
private void LoadHospitalXML()
{
try
{
FileStream fs = new FileStream("ConfigOrgHospital.xml", FileMode.Open, FileAccess.Read);
var xml = XmlReader.Create(fs);
rootElement = ConvertHospitalData(xml);
this.TreeViewCategories.ItemsSource = null;
List<HospitalWrapper> li = new List<HospitalWrapper>();
var hosp = rootElement.Items.FirstOrDefault();
if (hosp != null)
{
foreach (var i in hosp.Hospital)
{
li.AddIfNotNull(CreateHospList(i));
}
}
this.TreeViewCategories.ItemsSource = li;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
private HospitalWrapper CreateHospList(object obj)
{
var newItem = new HospitalWrapper();
newItem.Context = obj;
//Hospital Names//
if (obj is HospitalDataHospitalsHospital)
{
var hosp = (HospitalDataHospitalsHospital)obj;
//newItem.Title = "Hospitals";
newItem.Name = hosp.DefaultName;
var tmp = new HospitalWrapper();
tmp.Name = "Sites";
tmp.IsTitle = true;
if (hosp.Sites != null)
foreach (var i in hosp.Sites)
{
tmp.Items.AddIfNotNull(CreateHospList(i));
}
newItem.Items.Add(tmp);
tmp = new HospitalWrapper();
tmp.Name = "Specialties";
tmp.IsTitle = true;
if (hosp.Deps != null)
foreach (var j in hosp.Deps)
{
tmp.Items.AddIfNotNull(CreateHospList(j));
}
newItem.Items.Add(tmp);
}
}

Incidentally i did something like this just a few days ago. In my application a folder icon is added in the HierarchicalDataTemplate to those objects which behave like folders, i use a trigger to change the icon based on whether the item is expanded or not, here's the relevant bit of XAML:
<HierarchicalDataTemplate DataType="{x:Type data:FeedComposite}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal" Margin="1">
<StackPanel.Children>
<Image>
<Image.Style>
<Style BasedOn="{StaticResource IconImageStyleSmall}" TargetType="Image">
<Setter Property="Source" Value="{Binding Source={StaticResource Icon_FolderClosed}, Mode=OneTime}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsExpanded}" Value="True">
<Setter Property="Source" Value="{Binding Source={StaticResource Icon_FolderOpen}, Mode=OneTime}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock Text="{Binding Title}"/>
</StackPanel.Children>
</StackPanel>
</HierarchicalDataTemplate>
Where {StaticResource Icon_FolderOpen} and {StaticResource Icon_FolderClosed} are BitmapImages that hold the icons for the folder states. IconImageStyleSmall is a style which sets the MaxWidth and MaxHeight of the images to something appropriate.
Edit: For completion's sake.
<BitmapImage x:Key="Icon_FolderOpen" UriSource="pack://application:,,,/ImageResources/Icons/FolderOpen.ico" />
<BitmapImage x:Key="Icon_FolderClosed" UriSource="pack://application:,,,/ImageResources/Icons/FolderClosed.ico" />
<Style x:Key="IconImageStyleSmall" TargetType="Image">
<Setter Property="MaxWidth" Value="16"/>
<Setter Property="MaxHeight" Value="16"/>
<Setter Property="Margin" Value="1"/>
</Style>
Icons used

Related

WPF bind image via WCF to TileLayoutControl from ViewModel

My system is working like this.I have a WCF service,which is giving me ImageUrl and I am downloading this Image to my project directory and save it to my file system. Then I want to show this Image in Devexpress TileLayoutControl dynamically. But not showing anything on UI.Also I am not getting error. I tried to give ımage source like this ../MenuPhotos/imagename and like this /TileExample;component/MenuPhotos/imagename
My codes are like this;
XAML;
<dxlc:TileLayoutControl Name="TileList" AllowItemMoving="True" ItemsSource="{Binding Items}" ShowLayerSeparators="True" Padding="40,60,40,10" AllowLayerSizing="True" >
<dxlc:TileLayoutControl.Resources>
<Style TargetType="dxlc:Tile">
<Setter Property="BorderBrush" Value="Blue" />
<!--<Setter Property="Background" Value="{Binding BackgroundColor}" />-->
<Setter Property="Background" Value="{Binding BackgroundColor}" />
<Setter Property="Size" Value="{Binding SizeType}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Image Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Source>
<BitmapImage UriSource="/TileExample;component/MenuPhotos/restaurant1.jpg" />
</Image.Source>
</Image>
<Image Stretch="Uniform" Source="{Binding IconImageUrl}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Header" Value="{Binding}" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding BackgroundImageUrl}" FontFamily="Segoe UI Light" FontSize="12" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="dxlc:TileLayoutControl.IsFlowBreak" Value="{Binding IsFlowBreak}" />
<Setter Property="dxlc:TileLayoutControl.GroupHeader" Value="Menü" />
<Setter Property="dxwuin:Navigation.NavigateTo" Value="{Binding PageName}" />
<Setter Property="dxwuin:Navigation.NavigationParameter" Value="1" />
</Style>
<Style TargetType="dxlc:TileGroupHeader">
<Setter Property="Foreground" Value="#FFFFFFFF" />
</Style>
</dxlc:TileLayoutControl.Resources>
</dxlc:TileLayoutControl>
And my viewmodel like this;
IEnumerable<ImageItemsModel> items;
public GroupedItemsViewModel()
{
}
public IEnumerable<ImageItemsModel> Items
{
get { return items; }
private set { SetProperty<IEnumerable<ImageItemsModel>>(ref items, value, "Items"); }
}
public void LoadState(object navigationParameter)
{
MyServiceclient = new MyServiceclient();
var items = client.GetAllItems(null);
string remoteUri = "http://example.com/";
foreach (var item in items)
{
if (item.BackgroundImageUrl == null)
{
item.BackgroundImageUrl = "/TileExample;component/Assets/Images/user.jpg";
}
else
{
string remotefilepath = item.BackgroundImageUrl.Replace("../", "").Replace("//", "/");
string remotefilename = remotefilepath.Replace("img/integration/Dashboard/bg/", "");
string myStringWebResource = null;
using (WebClient myWebClient = new WebClient())
{
string path = #"C:\Users\USER\Documents\Visual Studio 2013\Projects\TileExample\TileExample\MenuPhotos\";
string checkpath = #"C:\Users\USER\Documents\Visual Studio 2013\Projects\TileExample\TileExample\MenuPhotos\" + remotefilename;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (!File.Exists(checkpath))
{
myStringWebResource = remoteUri + remotefilepath;
try
{
string downloadintopath = Path.Combine(path, remotefilename);
myWebClient.DownloadFile(myStringWebResource, downloadintopath);
}
catch { }
}
}
item.BackgroundImageUrl = item.BackgroundImageUrl.Replace("../../img/integration/Dashboard/bg", "../MemberPhotos");
}
}
Items = items;
}
#region INavigationAware Members
public void NavigatedFrom(NavigationEventArgs e)
{
}
public void NavigatedTo(NavigationEventArgs e)
{
LoadState(e.Parameter);
}
public void NavigatingFrom(NavigatingEventArgs e)
{
}
#endregion
In UI I couldnt saw nothing about my images.If I will include images as a source to visual studio its working.But I will fill to image as a service. So I cannot use this workaround.Thanks.
Hi recently I learn this solution:
In .NET 4.5 dynamic image path should be like this.
<Image Source="pack://application:,,,/TileExample;component/MenuPhotos/restaurant1.jpg"></Image>
This solution working for me.

WPF ContentControl content set in DataTemplate trigger randomly resets to default content

I'm having an issue with the content of a ContentControl whose content was set using DataTriggers randomly resetting the content to the default content specified in the DataTemplate.
The scenario is that I have a bunch of devices (sensors) out on the network that I need to check status on, among other things. Depending on what state the sensor is in I may want to show a colored circle (green, red or yellow), or an image. For example if the sensor is in use by someone I want to show an image representing a user. If the sensor is available for connection I want to show a green ellipse, etc.
I'm currently using a WPF DataGrid to display a list of sensors and their status, though I get the same erroneous behavior with a ListBox and ListView (haven't tried plain ItemsControl). FYI, sensors come and go asynchronously.
What you'll see if you run the sample code is that initially items with a connection state of CONNECTED will first display with the desired image. As rows get added to the grid the image randomly disappears and is replace with the default content specified in the DataTemplate. This problem only occurs when there is an image in the content. The other states work just fine.
Below is all the code (xaml, viewmodel, model) I think you'll need to see the behavior. Sorry for the amount of code posted below. I've tried to pair it down as much as possible to illustrate the issue. Hopefully the problem is obvious by looking at the XAML. The remaining source code will help you get it up and running more quickly if you so choose.
Here's the Window XAML:
<Window x:Class="StackOverflowGridIssue.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:StackOverflowGridIssue.Model"
xmlns:shape="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Width="500"
Title="MainWindow" >
<Grid>
<DataGrid Name="_SensorsDataGrid" ItemsSource="{Binding Sensors}"
AutoGenerateColumns="False" HeadersVisibility="Column" >
<DataGrid.Columns>
<!-- Status -->
<DataGridTemplateColumn Header="Status" MinWidth="50"
Width="SizeToHeader" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl x:Name="myContent" Background="LimeGreen"
Width="25" Height="25">
<ContentControl.ToolTip>
<TextBlock Text="{Binding ConnectionState, Mode=OneWay}"
Foreground="Black" />
</ContentControl.ToolTip>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}" BasedOn="{x:Null}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Ellipse Height="10" Width="10"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding Background}" />
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContentControl.Style>
</ContentControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.NOT_FOUND}">
<Setter TargetName="myContent" Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.AVAILABLE}">
<Setter TargetName="myContent" Property="Background"
Value="GREEN" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.CONNECTED}">
<Setter TargetName="myContent" Property="Content">
<Setter.Value>
<Image Source="Images/User.png" />
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- DEBUG Connection State -->
<DataGridTextColumn Header="DEBUG"
Binding="{Binding ConnectionState}" Width="SizeToCells" />
<!-- Sensor Name -->
<DataGridTextColumn Header="Sensor Name"
Binding="{Binding Name}" Width="SizeToCells" />
<!-- IPAddress -->
<DataGridTextColumn Header="IP Address"
Binding="{Binding IPAddress}" Width="SizeToCells" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Here's the App.xaml.cs where I bootstrap everything and simulate sensors being discovered asynchronously (fyi, the same issue occurs if I load them serially, it's just easier to see when the sensors are loaded slowly one at a time:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public const int NUM_SENSORS = 100;
Random _connectionStateGenerator = new Random();
ConnectionStateType _connectionState = ConnectionStateType.AVAILABLE;
SensorViewModel viewModel;
Timer _timer = new Timer(300);
int _index = 1;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Get a handle to the main view (MainWindow)
Window window = new MainWindow();
viewModel = new SensorViewModel();
//Loads sensors synchronously (same issue)
//AddSensors(viewModel.Sensors);
window.DataContext = viewModel;
window.Show();
//Simulate async sensor discovery (more real world example)
_timer.Enabled = false;
_timer.AutoReset = true;
_timer.Elapsed += (s, args) =>
{
Dispatcher.InvokeAsync(new Action( () =>
{
viewModel.Sensors.Add(
new Sensor("Sensor" + _index, "192.168.1." + _index,
(ConnectionStateType)_connectionStateGenerator.Next(0, 3)));
if (_index++ > NUM_SENSORS)
_timer.Enabled = false;
}));
};
_timer.Enabled = true;
}
//Helper for loading synchronously rather than asynchronously
private void AddSensors(ObservableCollection<Model.Sensor> sensors)
{
for (int i = 0; i < NUM_SENSORS; i++)
{
_connectionState = (ConnectionStateType)_connectionStateGenerator
.Next(0, 5);
sensors.Add(
new Sensor("Sensor" + i, "192.168.1." + i, _connectionState));
}
}
}
Here's the model code representing a sensor:
public enum ConnectionStateType
{
NOT_FOUND,
AVAILABLE,
CONNECTED,
}
public class Sensor : INotifyPropertyChanged
{
string _name = "Unknown";
string _IPAddress;
ConnectionStateType _connectionState = ConnectionStateType.AVAILABLE;
public Sensor(string name, string IPAddress, ConnectionStateType connectionState)
{
_name = name;
_IPAddress = IPAddress;
_connectionState = connectionState;
}
public ConnectionStateType ConnectionState
{
get { return _connectionState; }
set
{
if (value == _connectionState) return;
_connectionState = value;
NotifyPropertyChanged("ConnectionState");
}
}
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
NotifyPropertyChanged("Name");
}
}
public string IPAddress
{
get { return _IPAddress; }
set
{
if (value == _IPAddress) return;
_IPAddress = value;
NotifyPropertyChanged("IPAddress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
}
Here's the View Model:
public class SensorViewModel : INotifyPropertyChanged
{
ObservableCollection<Sensor> _sensors = new ObservableCollection<Sensor>();
public ObservableCollection<Sensor> Sensors
{
get { return _sensors; }
private set
{
if (value == _sensors) return;
_sensors = value;
NotifyPropertyChanged("Sensors");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
}
Thanks for your help.
The solution ended up being to have the trigger swap out the entire template rather than trying to set the Content area in the template.
Here is a cleaner piece XAML with template resources defined and a DataGrid that uses those resources:
<UserControl x:Class="StackOverflowGridIssue.SensorGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:model="clr-namespace:StackOverflowGridIssue.Model"
xmlns:shape="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<ControlTemplate x:Key="DefaultConnectionStateTemplate"
TargetType="{x:Type ContentControl}">
<Grid>
<Ellipse Height="10" Width="10"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="ConnectedTemplate"
TargetType="{x:Type ContentControl}" x:Shared="false">
<Grid>
<Image Source="Images/User.png" Height="25" Width="25"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<DataTemplate x:Key="SensorConnectionStateTemplate">
<ContentControl x:Name="myContent">
<ContentControl.ToolTip>
<TextBlock Text="{Binding ConnectionState, Mode=OneWay}" />
</ContentControl.ToolTip>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}" BasedOn="{x:Null}" >
<Setter Property="Template"
Value="{StaticResource DefaultConnectionStateTemplate}" />
</Style>
</ContentControl.Style>
</ContentControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.NOT_FOUND}">
<Setter TargetName="myContent" Property="Background" Value="Red" />
<Setter TargetName="myContent" Property="Content" Value="Not Found" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.AVAILABLE}">
<Setter TargetName="myContent" Property="Background" Value="GREEN" />
<Setter TargetName="myContent" Property="Content" Value="Available" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionState, Mode=OneWay}"
Value="{x:Static model:ConnectionStateType.CONNECTED}">
<Setter TargetName="myContent" Property="Template"
Value="{StaticResource ConnectedTemplate}" />
<Setter TargetName="myContent" Property="Content" Value="Connected" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DataGrid Name="_SensorsDataGrid" AutoGenerateColumns="False"
ItemsSource="{Binding Sensors}"
HeadersVisibility="Column" >
<DataGrid.Columns>
<!-- Status -->
<DataGridTemplateColumn Header="Status" MinWidth="50"
Width="SizeToHeader" IsReadOnly="True"
CellTemplate="{StaticResource SensorConnectionStateTemplate}" />
<!-- DEBUG Connection State -->
<DataGridTextColumn Header="DEBUG"
Binding="{Binding ConnectionState}" Width="SizeToCells" />
<!-- Sensor Name -->
<DataGridTextColumn Header="Sensor Name"
Binding="{Binding Name}" Width="SizeToCells" />
<!-- IPAddress -->
<DataGridTextColumn Header="IP Address" Width="SizeToCells"
Binding="{Binding IPAddress}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

WPF Menu binding using HierarchicalDataTemplate is not rendering menu items properly

I am building an WPF application trying to stick closely to MVVM principles. I am having a problem getting the menu to render correctly. I've tried a few approaches and am getting stuck. It seems like my binding is correct, but I'm not sure about my styles manipulation.
Here's the code I have the problem with. Like I said, it seems the binding is good, and I can even see the correct values for the Header menu items using Snoop, but all I see rendered is empty containers for menu items.
<DockPanel>
<DockPanel.Resources>
<HierarchicalDataTemplate x:Key="TopMenuHDT" ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Header" Value="{Binding MenuText}" />
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding MenuIcon}" Height="16px" Width="16px" />
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
</DockPanel.Resources>
<Menu DockPanel.Dock="Top" Height="auto"
ItemsSource="{Binding TopMenuItems}"
ItemTemplate="{StaticResource TopMenuHDT}"/>
In my main ViewModel:
private ObservableCollection<MenuViewModel> _topMenuItems;
public ObservableCollection<MenuViewModel> TopMenuItems
{
get { return _topMenuItems; }
set
{
if (_topMenuItems == value)
return;
_topMenuItems = value; base.RaisePropertyChanged("TopMenuItems");
}
}
...
public void LoadMainMenu()
{
IList<ViewModels.MenuViewModel> fileMenuItems = PopulateFileMenuEntries();
IList<ViewModels.MenuViewModel> editMenuItems = PopulateEditMenuEntries();
_topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_File", Children = new ObservableCollection<ViewModels.MenuViewModel>(fileMenuItems) });
_topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Edit", Children = new ObservableCollection<ViewModels.MenuViewModel>(editMenuItems) });
private IList<ViewModels.MenuViewModel> PopulateFileMenuEntries()
{
List<ViewModels.MenuViewModel> fileMenuItems = new List<ViewModels.MenuViewModel>();
fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Open", MenuIcon = new BitmapImage(new Uri("pack://application:,,,/Resources/OpenDocument16.png")) , Command = _mainWindowViewModel.OpenCommand });
fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "Open _Recent" });
return fileMenuItems;
}
MenuViewModel:
public class MenuViewModel : ObservableObject
{
internal MenuViewModel()
{
IsEnabled = true;
}
private string _menuText;
public string MenuText
{
get { return _menuText; }
set
{
if (_menuText == value)
return;
_menuText = value; base.RaisePropertyChanged("MenuText");
}
}
private ICommand _command;
public ICommand Command
{
get { return _command; }
set
{
if (_command == value)
return;
_command = value; base.RaisePropertyChanged("Command");
}
}
private BitmapImage _menuIcon;
public BitmapImage MenuIcon
{
get { return _menuIcon; }
set
{
if (_menuIcon == value)
return;
_menuIcon = value; base.RaisePropertyChanged("MenuIcon");
}
}
private ObservableCollection<MenuViewModel> _children;
public ObservableCollection<MenuViewModel> Children
{
get { return _children; }
set
{
_children = value; base.RaisePropertyChanged("Children");
}
}
}
Any help in getting this rendered correctly would be greatly appreciated.
EDIT:
Here's the final solution in case someone comes across this similar issue:
<DockPanel>
<Menu DockPanel.Dock="Top" Height="auto" ItemsSource="{Binding TopMenuItems}" >
<Menu.Resources>
<Image x:Key="MenuIconResource" Height="16" Width="16" Source="{Binding MenuIcon}" x:Shared="False" />
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Header" Value="{Binding MenuText}" />
<Setter Property="InputGestureText" Value="{Binding ShortcutText}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
<Setter Property="Icon" Value="{StaticResource MenuIconResource}" />
<Setter Property="ItemsSource" Value="{Binding Children}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate>
<Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.Resources>
</Menu>
Try this instead of your DataTemplate
<DockPanel>
<Menu DockPanel.Dock="Top" Height="auto"
ItemsSource="{Binding TopMenuItems}">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Header" Value="{Binding MenuText}" />
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding MenuIcon}" Height="16px" Width="16px" />
</Setter.Value>
</Setter>
<Setter Property="ItemsSource" Value="{Binding Children}"/>
</Style>
</Menu.Resources>
</Menu>
</DockPanel>

WPF Change ViewModel Property in View

I have a WPF DataGrid with several Templated Columns containing Checkboxes.
If I press a specific Checkbox the source of another checkbox has to be set.
I have a Viewmodel with a Property called Property (yes i know :)), and this Property has the Property "Visible".
If I check the "Mandatory" checkbox I want the "Visible" Value to be set and so the visible Checkbox to be set too, but somehow this is not working.
Heres my code:
<toolkit:DataGridTemplateColumn Header="Mandatory" IsReadOnly="False">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<CheckBox IsChecked="{Binding Path=Mandatory,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MandatoryDB}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MandatoryDB}" Value="False">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<ei:ChangePropertyAction TargetObject="{Binding UpdateSourceTrigger=PropertyChanged}" PropertyName="ReadOnly" Value="False" />
<ei:ChangePropertyAction TargetObject="{Binding NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" PropertyName="Visible" Value="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
Thanks,
Jonny
I think what you're trying to do is to drive your business logic by your View which is a horrible anti-pattern. What you need to is when the value of the "Mandatory" checkbox is changed then your viewmodel needs to set other properties accordingly and alert your view that there was a change (INotifyPropertyChanged).
Your view should not set any viewmodel properties, it should only read them and pass user input, the only other things which your view needs to controll are converters and other view-only items.
Hope this helps
see if this can help you.
lets assume that you want to show yes, no and ans in your Datagrid. for this find the sample code below.
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Coll}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Yes">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="50"
Height="50"
IsChecked="{Binding Yes,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="No">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="50"
Height="50"
IsChecked="{Binding No,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Ans">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle Width="100"
Height="50"
RadiusX="25"
RadiusY="25">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Ans}" Value="true">
<Setter Property="Fill" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Ans}" Value="false">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
code behind
public partial class YesNoTest : Window
{
public YesNoTest()
{
InitializeComponent();
if (Coll == null)
{
Coll = new ObservableCollection<Anss>();
for (int index = 0; index < 10; index++)
{
Coll.Add(new Anss() { Yes = false, No = false, Ans = true });
}
}
this.DataContext = this;
}
private ObservableCollection<Anss> _coll;
public ObservableCollection<Anss> Coll
{
get { return _coll; }
set { _coll = value; }
}
}
public class Anss : INotifyPropertyChanged
{
private bool _Yes;
public bool Yes
{
get
{
return _Yes;
}
set { _Yes = value; OnChanged("Yes"); OnChanged("Ans"); }
}
private bool _No;
public bool No
{
get
{
if (_No == true)
Yes = false;
return _No;
}
set { _No = value; OnChanged("No"); OnChanged("Ans"); }
}
private bool _Ans;
public bool Ans
{
get
{
if (Yes == true)
_Ans = true;
else
_Ans = false;
return _Ans;
}
set { _Ans = value; OnChanged("Ans"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}

How to add a right click context menu to Column Header for a WPF 4 DataGrid?

I want the context menu for a DataGrid's column headers to be different than the rest of the cells. So using the regular ContextMenu property is not going to work. There is DataGrid.RowHeaderTemplate, but I can't find DataGrid.ColumnHeaderTemplate.
Edit/Note: Columns are generated dynamically.
Target a common Style to all DataGridColumnHeaders:
<DataGrid.Resources>
<ContextMenu x:Key="DataGridColumnHeaderContextMenu" ...>
</ContextMenu>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource DataGridColumnHeaderContextMenu}" />
</Style>
</DataGrid.Resources>
If you want different context menus for different column headers then use triggers:
<DataGrid.Resources>
<ContextMenu x:Key="ColumnHeaderContextMenu1" ...>
</ContextMenu>
<ContextMenu x:Key="ColumnHeaderContextMenu2" ...>
</ContextMenu>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Triggers>
<Trigger Property="Content" Value="Column1">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu1}" />
</Trigger>
<Trigger Property="Content" Value="Column2">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu2}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
Hope that helps.
It depends on how you are generating your columns, if you are defining them in XAML then you could do this:
<DataGrid DataContext="{Binding MyDataContext}" ItemsSource="{Binding MyItems}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Age}" Header="Age" >
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{TemplateBinding Content}" >
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Task _1" />
<MenuItem Header="Task _2" />
<MenuItem Header="Task _3" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
If you want this to be done fully on code behind below is the approach.
using System.Windows.Controls.Primitives;
private void dgFormat_PreviewMouseRightButtonDown(object sender,
MouseButtonEventArgs e)
{
ContextMenu cm = new ContextMenu();
MenuItem miOne = new MenuItem();
miOne.Header = "One";
miOne.Click += MiOne_Click;
MenuItem miTwo = new MenuItem();
miTwo.Header = "Two";
miTwo.Click += MiTwo_Click;
cm.Items.Add(miOne);
cm.Items.Add(miTwo);
dgFormat.ContextMenu = cm;
dgFormat.ContextMenu.Visibility = Visibility.Collapsed;
DependencyObject depObj = (DependencyObject)e.OriginalSource;
while (depObj != null &&
!(depObj is DataGridColumnHeader))
{
depObj = VisualTreeHelper.GetParent(depObj);
}
if (depObj == null)
{
return;
}
if (depObj is DataGridColumnHeader)
{
DataGridColumnHeader colHeader = depObj as DataGridColumnHeader;
if (colHeader.Content.ToString() == "First Name")
{
dgFormat.ContextMenu.Visibility = Visibility.Visible;
}
}
}
private void MiOne_Click(object sender,
RoutedEventArgs e)
{
MessageBox.Show("One");
}
private void MiTwo_Click(object sender,
RoutedEventArgs e)
{
MessageBox.Show("Two");
}

Resources