WPF Data Templates and Buttons - wpf

Note: I am using Visual Studio 2012
I have the following DataTemplate (note: there is a TextBlock and Button)
<DataTemplate DataType="{x:Type ctlDefs:myButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
My screen is dynamically adding controls of myButton to an Items Control
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
>
The first time I render the view, the Textblock shows up for each button, but the Button itself does not. If I hide the view and then show it again, the button will appear sometimes (if the CommandButtons list does not change, if it changes, it never shows).
After rendering the view, I looked in the Output window and no binding errors.
What am I missing.

I've knocked up a quick application to work with your sample code and didn't seem to have any issues with the view. Below is what it looks like when run.
I've included my code below in case it helps. Note: I didn't add a real ICommand object for brevity and I accidentally named the MyButton class with capital M instead of small m like your example
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
this.CommandButtons = new ObservableCollection<MyButton>();
}
public ObservableCollection<MyButton> CommandButtons { get; private set; }
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainvm = new MainWindowViewModel();
var window = new MainWindow
{
DataContext = mainvm
};
window.Show();
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 1" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 2" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 3" });
}
}
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:ctlDefs="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type ctlDefs:MyButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
/>
</Grid>
MyButton.cs
public class MyButton : ViewModelBase
{
private string _labelText;
public string LabelText
{
get { return this._labelText; }
set { this._labelText = value; this.OnPropertyChanged("LabelText"); }
}
private double _buttonWidth;
public double ButtonWidth
{
get { return _buttonWidth; }
set { _buttonWidth = value; this.OnPropertyChanged("ButtonWidth"); }
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get { return _buttonCommand; }
set { _buttonCommand = value; this.OnPropertyChanged("ButtonCommand"); }
}
}

Related

Command not executing when clicking a button

How should the collection be bound to Listview and have the button execute the command to add a single user to the collection.
View Model Code:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class UserViewModel : ViewModelBase
{
private ObservableCollection<User> _UsersList;
private User _user;
public UserViewModel()
{
_user = new User();
_UsersList = new ObservableCollection<User>();
_UsersList.CollectionChanged += _UsersList_CollectionChanged;
}
private void _UsersList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
}
public ObservableCollection<User> Users
{
get { return _UsersList; }
set
{
_UsersList = value;
OnPropertyChanged("Users");
}
}
public User User
{
get
{
return _user;
}
set
{
_user = value;
OnPropertyChanged("User");
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater(this);
return mUpdater;
}
//set
//{
// mUpdater = value;
//}
}
private void Submit()
{
Users.Add(User);
User = new User();
}
private class Updater : ICommand
{
#region ICommand Members
UserViewModel _obj;
public Updater(UserViewModel obj)
{
_obj = obj;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_obj.Submit();
}
}
View Xaml:
<ListView Name="UserGrid"
Grid.Row="1"
Margin="4,178,12,13"
ItemsSource="{Binding Users}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="UserId"
DisplayMemberBinding="{Binding UserId}"
Width="50"/>
</GridView>
</ListView.View>
</ListView>
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Name"
HorizontalAlignment="Center"/>
<TextBox Grid.Row="0"
Grid.Column="1"
Width="100"
HorizontalAlignment="Center"
Text="{Binding User.UserId, Mode=TwoWay}"/>
<Button Content="Update"
Grid.Row="1"
Height="23"
HorizontalAlignment="Left"
Margin="310,40,0,0"
Name="btnUpdate"
VerticalAlignment="Top"
Width="141"
Command="{Binding Path=UpdateCommad}" />
The error is in the binding of the button to the command:
Command="{Binding Path=UpdateCommad}" />
It should be:
Command="{Binding Path=UpdateCommand}" />
To find errors like this always read the Debug Output. In this case it quickly showed:
System.Windows.Data Error: 40 : BindingExpression path error:
'UpdateCommad' property not found on 'object' ''UserViewModel'
To prevent this from happening at all you could use Xaml code completion by setting the design-time DataContext type:
<Window x:Class="TestWpfApplication.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:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525"
d:DataContext="{d:DesignInstance Type=local:UserViewModel}">
After setting this you will have code-completion in Xaml: just start typing the binding path and it will offer valid options.

WPF DataGrid SelectionChange event doesn't fire when ItemsSourse is being set from UserControl

I have very interesting scenario here, look:
MainWindow XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TabControl Grid.Row="0"
Grid.Column="0"
SelectionChanged="Selector_OnSelectionChanged">
<TabItem Header="First"/>
<TabItem Header="Second"/>
</TabControl>
<ContentPresenter Grid.Column="1"
Content="{Binding SelectedUserControl}">
</ContentPresenter>
</Grid>
UserControlOne XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
ItemsSource="{Binding DataSource}"
SelectedItem="{Binding SelectedItem}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<Command:EventToCommand Command="{Binding SelectionChangedCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
<Button Grid.Row="1"
Content="SetSource"
Height="50"
Command="{Binding SetSourceCommand}"/>
<Button Grid.Row="2"
Content="RemoveSource"
Height="50"
Command="{Binding RemoveSourceCommand}"/>
</Grid>
UserContolTwo XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Content="SetSource"
Height="50"
Command="{Binding SetSourceCommand}"/>
<Button Grid.Row="1"
Content="RemoveSource"
Command="{Binding RemoveSourceCommand}"
Height="50"/>
</Grid>
CodeBehind:
public class GridItem
{
public String Name { get; set; }
public override string ToString()
{
return Name;
}
}
public partial class Window1 : Window, INotifyPropertyChanged
{
private List<GridItem> _items;
private GridItem _selectedItem;
private List<GridItem> _dataSource;
private readonly List<UserControl> _userControlList;
private UserControl _selectedUserControl;
public UserControl SelectedUserControl
{
get { return _selectedUserControl; }
set
{
_selectedUserControl = value;
RaisePropertyChanged("SelectedUserControl");
}
}
public GridItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
public List<GridItem> DataSource
{
get { return _dataSource; }
set
{
_dataSource = value;
RaisePropertyChanged("DataSource");
}
}
public Window1()
{
InitializeComponent();
DataContext = this;
_items = new List<GridItem>
{
new GridItem { Name = "Igor" },
new GridItem { Name = "Vasya"},
new GridItem { Name = "Vladlen"}
};
_userControlList = new List<UserControl>
{
new UserControl1(),
new UserControl2()
};
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedUserControl = _userControlList[((TabControl)sender).SelectedIndex];
}
#region SetSourceCommand
private RelayCommand<Object> _setSourceCommand;
public RelayCommand<Object> SetSourceCommand
{
get
{
return _setSourceCommand ?? (_setSourceCommand =
new RelayCommand<Object>(SetSourceMethod));
}
}
private void SetSourceMethod(Object obj)
{
DataSource = _items;
SelectedItem = _items.FirstOrDefault();
}
#endregion SetSourceCommand
#region RemoveSourceCommand
private RelayCommand<Object> _removeSourceCommand;
public RelayCommand<Object> RemoveSourceCommand
{
get
{
return _removeSourceCommand ?? (_removeSourceCommand =
new RelayCommand<Object>(RemoveSourceMethod));
}
}
private void RemoveSourceMethod(Object obj)
{
DataSource = null;
}
#endregion RemoveSourceCommand
#region SelectionChangedCommand
private RelayCommand<Object> _selectionChangedCommand;
public RelayCommand<Object> SelectionChangedCommand
{
get
{
return _selectionChangedCommand ?? (_selectionChangedCommand =
new RelayCommand<Object>(SelectionChangedMethod));
}
}
private void SelectionChangedMethod(Object obj)
{
Debug.WriteLine("Event have been rised! Selected item is {0}", ((DataGrid)obj).SelectedItem ?? "NULL");
}
#endregion RemoveSourceCommand
}
I have MainWindow, that contains UserControl. UserControl is being set dinamically, so if you pick up FirstTabItem, then UserControlOne will be loaded and if you pick up SecondTabItem - UserControlTwo will be loaded into ContentPresenter of MainWindow.
If I click button SetSource on FirstTabItem (actually on UserControlOne) - then SelectionChanged event of DataGrid fires as usually. But if I click button SetSource on SecondTabItem (actually on UserControlTwo) - then SelectionChanged event of DataGrid doesn't fire at all. Despite on bouth buttons bound to the same command (SetSourceCommand).
If buttons don't placed on other controls, for example, only on different tabitems of the same TabControl - bouth buttons invokes SelectionChange event. So problem really in markup, in using UserControls.
Has anyone encoutered with that problem? How can I fix it? I don't want invoke eventhandler programmatically.
I posted all the required code here, so you can copy-paste it and try by yourself very quickly. Or I can load example project if someone interested.
Okay so here is the actual problem in your code.
The UserControl1 has a grid in which you have used the SelectedItem Dependency Property. On this specific DP you have a Command SelectionChangedCommand.
In you commands SetSourceCommand and RemoveSourceCommand you are updating SelectedItem of the data grid which fires the SelectionChangedCommand because of the event SelectionChanged.
In your UserControl2 there is no data grid nor any control which fires SelectionChanged event to call SelectionChangedCommand. Hence it is never executed.

Error Template Design

It seems like I read another question / answer on this site about this issue but I cannot recall what the answer was and now I cannot find the original post.
I am not a fan of the default error template in WPF. I understand how to change this error template. However, if I add some content to the end of, say, a textbox, the size of the textbox does not change and the added content will (potentially) get clipped. How do I alter the textbox (I believe the correct termonology is adorned element) in this scenario so that nothing gets clipped?
Here is the XAML for the error template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<TextBlock Foreground="Red" Text="Error..." />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the XAML for a couple of textboxes in the form:
<StackPanel>
<TextBox Text="{Binding...}" />
<TextBox />
</StackPanel>
Here's a solution adapted from Josh Smith's article on Binding to (Validation.Errors)[0] without Creating Debug Spew.
The trick is to define a DataTemplate to render the ValidationError object and then use a ContentPresenterto display the error message. If there is no error, then the ContentPresenter will not be displayed.
Below, I have shared the code of the sample app that I created.
Here is the XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow">
<StackPanel Margin="5">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type ValidationError}">
<TextBlock Text="{Binding ErrorContent}" Foreground="White" Background="Red" VerticalAlignment="Center" FontWeight="Bold"/>
</DataTemplate>
</StackPanel.Resources>
<TextBox Name="TextBox1" Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox1, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<TextBox Name="TextBox2" Text="{Binding Text2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox2, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<Button Content="Validate" Click="Button_Click"/>
</StackPanel>
</Window>
The code behind file:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel _ViewModel = null;
public MainWindow()
{
InitializeComponent();
_ViewModel = new ViewModel();
DataContext = _ViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_ViewModel.Validate = true;
_ViewModel.OnPropertyChanged("Text1");
_ViewModel.OnPropertyChanged("Text2");
}
}
}
The ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication1
{
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private string _Text1;
public string Text1
{
get { return _Text1; }
set
{
_Text1 = value;
OnPropertyChanged("Text1");
}
}
private string _Text2;
public string Text2
{
get { return _Text2; }
set
{
_Text2 = value;
OnPropertyChanged("Text2");
}
}
public bool Validate { get; set; }
#region INotifyPropertyChanged Implemenation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region IDataErrorInfo Implementation
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
string errorMessage = string.Empty;
if (Validate)
{
switch (columnName)
{
case "Text1":
if (Text1 == null)
errorMessage = "Text1 is mandatory.";
else if (Text1.Trim() == string.Empty)
errorMessage = "Text1 is not valid.";
break;
case "Text2":
if (Text2 == null)
errorMessage = "Text2 is mandatory.";
else if (Text2.Trim() == string.Empty)
errorMessage = "Text2 is not valid.";
break;
}
}
return errorMessage;
}
}
#endregion
}
}

Listbox Binding Problem

Please solve my problem, my listbox don't bind, i don't why :(
class TLocation
{
public string name;
}
Main Window:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TLocation}" x:Key="myTaskTemplate">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="1" Name="textBlock1" Text="{Binding Path=name, FallbackValue=name}" VerticalAlignment="Top" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Height="131" HorizontalAlignment="Left" Margin="29,44,0,0" Name="listBox1" VerticalAlignment="Top" Width="200" ItemTemplate="{StaticResource myTaskTemplate}" />
</Grid>
</Window>
Main Window Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<TLocation> list = new List<TLocation>();
TLocation temp = new TLocation();
temp.name = "hi";
list.Add(temp);
listBox1.ItemsSource = list;
}
}
name is a field, you can only bind to public properties though. You might also want to implement INotifyPropertyChanged if the name changes at runtime. If you are new to databinding make sure to read the overview.
e.g.
public class TLocation : INotifyPropertyChanged
{
private string _name = null;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
(Binding is also case senstitive and your capitalization is not following the conventions so i changed it in my example, here the Binding.Path would need to be Name.)

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