Change button image when context menu item is selected - wpf

Can anyone tell me how to change button image when context menu Item is clicked?
I have a button with image and context menu in it. I want to change the image of the button, everytime I click contextmenu item.
With the following piece of code I am able to display contextmenu items on right click. But don't know how to proceed further.
Can anyone guide me ?
I tried using command strangely command never got called.
<Button Background="Gray" Name="statusBtn" VerticalAlignment="Top" HorizontalAlignment="Right" FontWeight="Bold" Foreground="Red">
<DockPanel >
<Image DockPanel.Dock="Top" Stretch="Fill" Source="{Binding ToEnum, Converter={StaticResource EnumToImgConverter}}" Height="37" Width="72" />
<TextBlock HorizontalAlignment="Center" Margin="0,23,1,2">Test</TextBlock>
</DockPanel>
<Button.ContextMenu>
<ContextMenu Name="ContextMenuName" ItemsSource="{Binding Path=Popuplistitems}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Header="{Binding Message}" Command="{Binding popupListCommand}">
<MenuItem.ItemContainerStyle >
<Style TargetType="MenuItem">
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Button.ContextMenu>
</Button>

First, I think you need to change your MenuItem.ItemContainerStyle to just MenuItem.Style - I believe the ItemContainerStyle is meant for the sub-menuitems that you can place into the MenuItem when you want nested context menus.
I was able to get the content of the button to change with the following code. I used text content, but it should be easy to swap it out for image content:
The xaml:
<Window x:Class="ChangeButtonContent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Height="30" Width="200">
<Button.Content>
<DockPanel >
<TextBlock Text="{Binding ChangingText, Mode=TwoWay}" />
</DockPanel>
</Button.Content>
<Button.ContextMenu>
<ContextMenu Name="ContextMenuName">
<MenuItem Command="{Binding ChangeTextCommand}" Header="ChangeText"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
The Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
ChangingText = "Initial Text";
ChangeTextCommand = new MyCommand((notUsed) =>
{
ChangingText = "Text After Context Menu Click";
});
}
private string _changingText;
public string ChangingText
{
get
{
return _changingText;
}
set
{
_changingText = value;
OnPropertyChanged("ChangingText");
}
}
public MyCommand ChangeTextCommand
{
get;
private set;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MyCommand : ICommand
{
private Action<object> _action;
public MyCommand(Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}

Related

Adding Icon to Dynamic Menu item in WPF

I am trying to generate MenuItem dynamically.
How can I Bind that in Style?
Here is the code.
XAML
<Window.Resources>
<Image x:Key="Image.Icon"
Source="pack://application:,,,/DynamicMenu;component/icon.png"/>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItems}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding ImagePath}" Width="12" Height="12" />
</Setter.Value>
</Setter>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}"
ItemsSource="{Binding Path=MenuItems}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}"/>
</StackPanel>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<Grid>
</Grid>
</DockPanel>
ViewModel
public class MenuItemViewModel
{
private readonly ICommand _command;
public MenuItemViewModel()
{
_command = new CommandViewModel(Execute);
}
public string Header { get; set; }
public string Param1 { get; set; }
public string ImagePath { get; set; }
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get {return _command; }
}
private void Execute()
{
MessageBox.Show("Clicked at " + Header + Param1);
}
}
Command
public class CommandViewModel : ICommand
{
private readonly Action _action;
public CommandViewModel(Action action)
{
_action = action;
}
public void Execute(object o)
{
_action();
}
public bool CanExecute(object o)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
}
I want to add a different icon for different MenuItem.
So I am planning to pass the icon file as MenuItemViewModel property.
Need a way to bind the icon property to the MenuItem.
Thanks.
I find a solution for my question:
<MenuItem Header="{Binding Path=Header}" Command="{Binding PresentationTripLegCommand}">
<MenuItem.Icon>
<Image Source="{Binding IconFileName}" Height="16" />
</MenuItem.Icon>
</MenuItem>

WPF PropertyChanged event is null after updating property

I'm struggling with some databinding. In my MainWindow I have 2 buttons which are databound to a property Result in a class "Properties" - strictly for holding properties that I will be using for databinding. The buttons are hidden by default, and when I want them to become visible I simply set Result property that they are bound to to "True"
I know the databinding is working because if I set the property to a static value, the buttons are visible / not visible. See below for my XAML
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</StackPanel.Resources>
<Button x:Name="btnBack" Height="25" Content="<- Back" Visibility="{Binding Path=Result, Converter={StaticResource BoolToVis},
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="75" Click="btnBack_Click" Margin="0,0,10,0" />
<Button x:Name="btnNext" Height="25" Content="Next ->" Visibility="{Binding Path=Result, Converter={StaticResource BoolToVis},
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="75" Click="btnNext_Click" Margin="10,0,0,0"/>
</StackPanel>
So they are bound to "Result" property, and I have UpdateSourceTrigger=Propertychangedin my binding.
In my "Properties" class I have the below and AM implementing INotifyPropertyChanged
bool _result;
#endregion
public bool Result {
get
{
return _result;
}
set
{
_result = value;
NotifyPropertyChanged("Result");
}
}
#region EVENTS
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
But for some reason when I change the property to "True" the PropertyChanged Event is null and therefore the event never fires.
Any idea as to why this is happening? Could it be because this code isn't in my ViewModel and just in a separate class?
Make sure that you have set the DataContext of the window to an instance of your Properties class and that you don't set the DataContext property of any parent element of the StackPanel to something else because the DataContext is inherited.
Please refer to the following sample code. The Buttons do become visible as expected after the 3 second delay:
public partial class MainWindow : Window
{
private Properties _viewModel = new Properties();
public MainWindow()
{
InitializeComponent();
DataContext = _viewModel;
this.Loaded += async (s, e) =>
{
await Task.Delay(3000);
_viewModel.Result = true;
};
}
}
public class Properties : INotifyPropertyChanged
{
bool _result;
public bool Result
{
get
{
return _result;
}
set
{
_result = value;
NotifyPropertyChanged("Result");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</StackPanel.Resources>
<Button x:Name="btnBack" Height="25" Content="<- Back" Visibility="{Binding Path=Result, Converter={StaticResource BoolToVis},
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="75" Margin="0,0,10,0" />
<Button x:Name="btnNext" Height="25" Content="Next ->" Visibility="{Binding Path=Result, Converter={StaticResource BoolToVis},
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="75" Margin="10,0,0,0"/>
</StackPanel>
</Window>

Silverlight 5 ListBox IsSelected style binding broken?

i see Silverlight 5 bought style binding. Tried to apply it in a ListBox control, for multiple selection. I have the following XAML ListBox (the code works in a WPF application).
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="ListBoxItem">
<TextBlock Text="{Binding DisplayValue}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When i run this i get a binding error, it seems that it tries to find the IsSelected property on the type of "Values" collection instead of each individual item from that collection. Has anyone else experience this?
Update
Added full code to reproduce, you need to scroll the listbox to see the error in the output log
public class ValueViewModel : INotifyPropertyChanged
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
private string _displayValue;
public string DisplayValue
{
get { return _displayValue; }
set
{
_displayValue = value;
OnPropertyChanged("DisplayValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<ValueViewModel> _values;
public ObservableCollection<ValueViewModel> Values
{
get { return _values; }
set
{
_values = value;
OnPropertyChanged("Values");
}
}
public MainPageViewModel()
{
Values = new ObservableCollection<ValueViewModel>();
for (var i = 0; i < 50; i++)
Values.Add(new ValueViewModel() { DisplayValue = i.ToString(), IsSelected = (i % 5) == 0 });
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the XAML:
<Grid x:Name="LayoutRoot" Background="White" >
<Grid.Resources>
<viewmodels:MainPageViewModel x:Key="vmMainPage"/>
</Grid.Resources>
<Grid x:Name="workGrid" DataContext="{Binding Source={StaticResource vmMainPage}}">
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5" Text="{Binding DisplayValue}"/>
<TextBlock Margin="5" Text="{Binding IsSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Update 2
It seems the problem around the error is that in a scrollable situation if you select items 1 and then scroll down and select item 49 (in the above example), the 1st selection is lost.
I cannot reproduce it. It works fine for me. Here is a full working example based on your code. One issue that I did notice though is that when a ListBoxItem is rendered it automatically sets the property on the data object to false, regardless of whether it was true to begin with. So if you load up a list and set some of it's items to be pre-selected, all the items will be unselected when the ListBoxItems are rendered. One way to prevent this is to use Dispatcher.BeginInvoke and set the selected items there. See my comments in the code below.
XAML:
<UserControl x:Class="SilverlightApplication12.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRoot"
Background="White">
<ListBox ItemsSource="{Binding Entities}"
SelectionMode="Multiple">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="ListBoxItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Margin="10 0 0 0"
Text="IsSelected:" />
<TextBlock Margin="5 0 0 0"
Text="{Binding IsSelected}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code-behind + entity class:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightApplication12
{
public partial class MainPage : UserControl, INotifyPropertyChanged
{
private ObservableCollection<MyEntity> _Entities;
public ObservableCollection<MyEntity> Entities
{
get { return _Entities; }
set
{
_Entities = value;
OnPropertyChanged("Entities");
}
}
public MainPage()
{
InitializeComponent();
Entities = new ObservableCollection<MyEntity>();
Entities.Add(new MyEntity()
{
Name = "One",
IsSelected = false,
});
Entities.Add(new MyEntity()
{
Name = "Two",
IsSelected = true,
//Even though this is initially true it does not matter.
//When the ListBoxItem is rendered it sets the property to false.
});
Entities.Add(new MyEntity()
{
Name = "Three",
IsSelected = false,
});
LayoutRoot.DataContext = this;
//Enable the following line to set the 2nd item to selected when the page is loaded.
//Dispatcher.BeginInvoke(() => Entities[1].IsSelected = true);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MyEntity : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
private bool _IsSelected;
public bool IsSelected
{
get
{
return _IsSelected;
}
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Everything except this <DataTemplate DataType="ListBoxItem"> looks fine to me.
If the Values collection is a collection of ListBoxItems, you don't need the IsSelected binding.
Otherwise, the DataType on your DataTemplate is wrong and should probably be left blank.
So i've managed to find a workarround that seems to do the job for my needs. It will set the already loaded values once the Loaded event gets triggered. And it wraps the MouseDown event to set the selection status. It's not a true databind but gets the job done, and still keeps the View clean of code.
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding DisplayValue, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
PropertyName="IsSelected"
Value="{Binding IsSelected}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ei:ChangePropertyAction
TargetObject="{Binding}"
PropertyName="IsSelected"
Value="{Binding IsSelected, Converter={StaticResource invertBooleanConverter}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

SelectedItem of ListBox with DataTemplate

I have a ListBox:
<ListBox Name="lbsfHolder"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:SupportingFunction GotFocus="SupportingFunction_GotFocus"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the ViewModel I have:
private SupportingFunction _selectedSupportedFunction;
public SupportingFunction SelectedSupportedFunction
{
get { return _selectedSupportedFunction; }
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
But when I'm trying to select any item in list box nothing happens. The SelectedItem is null for the ListBox and for SelectedValue, too. Do I need to add some special code to make this work?
UPD:
I've changed views a bit, now I have:
<UserControl x:Class="RFM.UI.WPF.Controls.SupportingFunction">
<Grid>
<ListBox Name="supportingFunctions"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBox Name="tbsfName" Grid.Column="0" Text="{Binding Path=Title, Mode=TwoWay}"></TextBox>
<TextBox Name="tbsfExperssion" Grid.Column="1" Text="{Binding Path=Expression}" HorizontalAlignment="Stretch"></TextBox>
<Button Name="bsfDel" Grid.Column="2">Del</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
In Page where this control placed:
<StackPanel Name="spSupportingFunctions">
<StackPanel Name="spsfOperations" Orientation="Horizontal">
<Button Name="bsfAdd" Width="30" Command="commands:CustomCommands.AddSupportingFunction">Add</Button>
</StackPanel>
<controls:SupportingFunction DataContext="{Binding Self}" />
</StackPanel>
at code behind of this Page
public PlotDataPage()
{
DataContext = new PlotDataViewModel();
InitializeComponent();
}
and this is the full listing of PlotDataViewModel
public class UISupportingFunction : ISupportingFunction
{
public string Title { get; set; }
public string Expression { get; set; }
}
public class PlotDataViewModel : INotifyPropertyChanged
{
public PlotDataViewModel Self
{
get
{
return this;
}
}
private ObservableCollection<UISupportingFunction> _supportingFunctions;
public ObservableCollection<UISupportingFunction> UISupportingFunctions
{
get
{
return _supportingFunctions;
}
set
{
_supportingFunctions = value;
NotifyPropertyChanged("UISupportingFunctions");
}
}
private UISupportingFunction _selectedSupportedFunction;
public UISupportingFunction SelectedSupportedFunction
{
get
{
return _selectedSupportedFunction;
}
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
public PlotDataViewModel()
{
UISupportingFunctions = new ObservableCollection<UISupportingFunction>();
}
public void CreateNewSupportingFunction()
{
UISupportingFunctions.Add(new UISupportingFunction() { Title = Utils.GetNextFunctionName() });
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I'm just calling the CreateNewSupportingFunction() method when I click Add button. Everything looks fine - the items is add and I see them. But when I'm clicking on one of the TextBoxes and then to the bsfDel button right to each item I'm getting null in SelectedSupportedFunction.
Maybe it is because of focus event have been handling by TextBox and not by ListBox?
It's either your ItemsSource UISupportingFunctions is not a SupportingFunction object or you did not set the View's Datacontext to your ViewModel.
ViewModel.xaml.cs
this.DataContext = new ViewModelClass();

Data Binding : Child accessing AncestorType property

Bellow is the code behind and the Xaml for a demo app to review databing and wpf.
The problem is binding Store.ImagePath property to the person node is not working. That is the image is not showing.
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
Here is the code-behind
namespace TreeViewDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Customers customers = new Customers();
customers.Users = new List<Person>
{
new Person { Name = "John"},
new Person { Name = "Adam"},
new Person { Name = "Smith"}
};
Store store = new Store();
store.AllCustomers.Add(customers);
this.DataContext = store;
}
}
public class Store : INotifyPropertyChanged
{
string imagePath = "imageone.png";
public Store()
{
AllCustomers = new ObservableCollection<Customers>();
}
public string StoreName
{
get
{
return "ABC Store";
}
}
public ObservableCollection<Customers> AllCustomers
{
get;
set;
}
public string ImagePath
{
get
{
return imagePath;
}
set
{
if (value == imagePath) return;
imagePath = value;
this.OnPropertyChanged("ImagePath");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customers
{
public string Label
{
get
{
return string.Format("People({0})", Users.Count());
}
}
public List<Person> Users
{
get;
set;
}
}
public class Person : INotifyPropertyChanged
{
public string Name
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and here is the Xaml.
<Window x:Class="TreeViewDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewDemo"
Title="MainWindow" Height="350" Width="525">
<Window.Resources >
<DataTemplate DataType="{x:Type local:Person}" x:Key="personKey" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="customerKey" ItemsSource="{Binding Users}" ItemTemplate="{StaticResource personKey }" >
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Canvas>
<Button HorizontalAlignment="Left" DockPanel.Dock="Top" Height="29" Width="112" Canvas.Left="123" Canvas.Top="5">Image one</Button> <Button HorizontalAlignment="Left" VerticalAlignment="Top" DockPanel.Dock="Top" Height="28" Width="119" Canvas.Left="249" Canvas.Top="7">Image two</Button>
<TreeView HorizontalAlignment="Stretch" Name="treeView1" VerticalAlignment="Stretch"
ItemsSource="{Binding .}" Height="260" Width="363" Canvas.Left="81" Canvas.Top="45">
<TreeViewItem ItemsSource="{Binding AllCustomers}" ItemTemplate="{StaticResource customerKey}" Header="{Binding StoreName}"></TreeViewItem>
</TreeView>
</Canvas>
</Grid>
</Window>
All files are in the same directory.
Thanks
A relative source is used to look up an object in the visual tree. You're asking it to find the nearest Store in the visual tree. Since a Store cannot even be in the visual tree, the lookup will fail and yield null. What you actually want is the DataContext of the root Window, since that is where your Store is held:
<Image Source="{Binding DataContext.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

Resources