Bind WPF properties in differents contexts
-
13-10-2022 - |
Question
I'm developping a WPF app and ran into a problem about property binding. I designed a main menu for my app and I would like to have a random left margin value for each menu entries (one menu item is a button). I also redefine Button template. So far, here is my XAML code :
The MainMenuView.xaml :
<UserControl x:Class="OfficeTourismeBrantome.Views.MainMenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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="800" d:DesignWidth="300">
<UserControl.Resources>
<Style x:Key="MenuItemButtonStyle" TargetType="Button">
<Setter Property="FontSize" Value="35" />
<Setter Property="FontFamily" Value="Segoe" />
<Setter Property="Foreground" Value="#FFEBEDEA" />
<Setter Property="HorizontalContentAlignment" Value="Right" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Canvas HorizontalAlignment="Stretch" Background="Aqua">
<ContentPresenter Canvas.Left="{Binding MenuLayout.MenuItemLeftMargin}" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ItemsControl Name="menuButtonContainer" ItemsSource="{Binding Items}" Margin="{Binding MenuLayout.MenuMargin}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Style="{StaticResource ResourceKey=MenuItemButtonStyle}"
Margin="{Binding ElementName=menuButtonContainer,
Path=DataContext.MenuLayout.MenuItemMargin}"
Height="{Binding ElementName=menuButtonContainer,
Path=DataContext.MenuLayout.MenuItemSize.Height}"
Content="{Binding Text}"
Command="{Binding ElementName=menuButtonContainer,
Path=DataContext.ChangeThemeCommand}"
CommandParameter="{Binding Id}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see, I got 2 differents levels here :
- The first level, which DataContext is UserControl one (MainMenuViewModel)
- The second one, which DataContext is inside the ItemControl that list MenuItems
On this code, only 1 thing is not working : The Canvas ContentPresenter Canvas.Left property binding :
<ContentPresenter Canvas.Left="{Binding MenuLayout.MenuItemLeftMargin}" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
My questions :
- Why the above binding not working? In step-by-step, I see the random value correctly assigned. There is no change on runtime so far, so I didn't implement INotifyPropertyChanged (actually all the others bindings work as expected).
- I realized that some of the properties in ItemTemplate (such as Height and Margin) should go in Style definition. When I try it, it doesn't bind...
I do it this way :
<UserControl.Resources>
<Style x:Key="MenuItemButtonStyle" TargetType="Button">
<Setter Property="Height" Value="{Binding MenuLayout.MenuItemSize.Height}" />
</Style>
</UserControl.Resources>
What should I put to make this binding working?
THanks for your answers !
Here is the ViewsModels :
MainMenuViewModel reduced to utile stuff (exposes MenuLayout property):
class MainMenuViewModel : LanguageChangedNotificationViewModelBase
{
private MenuLayout _menuLayout;
/// <summary>
/// Menu items list property
/// </summary>
public ObservableCollection<MenuItem> Items
{
get { return _items; }
set { _items = value; }
}
public MenuLayout MenuLayout
{
get
{
return _menuLayout;
}
set
{
_menuLayout = value;
}
}
/// <summary>
/// Constructor (that calls base constructor)
/// </summary>
public MainMenuViewModel() : base()
{
// New menu items list instance
Items = new ObservableCollection<MenuItem>();
// Menu layout var needed for display
MenuLayout = new MenuLayout();
}
} }
And MenuLayout class :
namespace OfficeTourismeBrantome.Models
{ class MenuLayout { private Thickness _menuMargin;
private Thickness _menuItemMargin;
private Size _menuItemSize;
public Thickness MenuItemMargin
{
get {
_menuItemMargin.Left = GetRandomNumber();
return _menuItemMargin;
}
set { _menuItemMargin = value; }
}
public Thickness MenuMargin
{
get { return _menuMargin; }
set { _menuMargin = value; }
}
public Size MenuItemSize
{
get { return _menuItemSize; }
set { _menuItemSize = value; }
}
public double MenuItemLeftMargin
{
get
{
return GetRandomNumber();
}
}
public MenuLayout()
{
MenuItemMargin = new Thickness(Properties.Settings.Default.mainMenuItemMarginLeft, Properties.Settings.Default.mainMenuItemMarginTop, Properties.Settings.Default.mainMenuItemMarginRight, Properties.Settings.Default.mainMenuItemMarginBottom);
MenuMargin = new Thickness(Properties.Settings.Default.mainMenuMarginLeft, Properties.Settings.Default.mainMenuMarginTop, Properties.Settings.Default.mainMenuMarginRight, Properties.Settings.Default.mainMenuMarginBottom);
MenuItemSize = new Size(100, 50);
}
private double GetRandomNumber()
{
double minimum = 0;
double maximum = Properties.Settings.Default.mainMenuItemMarginLeft;
Random random = new Random();
return random.NextDouble(); //* (maximum - minimum) + minimum;
}
}
}
No correct solution