I'm changing the position of a UIElement within a WPF Canvas by using the static Canvas.SetTop
method in the code-behind (in the full application I'm using a complex Rx chain but for this example I've simplified it to a button click).
The problem I have is that the value of the attached property, Canvas.Top in the XAML, is bound to a property in my ViewModel. Calling Canvas.SetTop
bypasses the set
in my ViewModel so I don't get the updated value. How can I update the Canvas.Top value in the code-behind so that the ViewModel properties' setter is called?
XAML View:
<Window x:Class="WpfApplication1.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<Canvas>
<Button Content="Move Button" Canvas.Top="{Binding ButtonTop}" Click="ButtonBase_OnClick" />
</Canvas>
</Grid>
</Window>
Code-behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindowView : Window
{
public MainWindowView()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Canvas.SetTop((UIElement) sender, Canvas.GetTop((UIElement) sender) + 5);
}
}
}
ViewModel:
using System.Windows;
namespace WpfApplication1
{
public class MainWindowViewModel : DependencyObject
{
public static readonly DependencyProperty ButtonTopProperty = DependencyProperty.
Register("ButtonTop", typeof(int), typeof(MainWindowViewModel));
public int ButtonTop
{
get { return (int) GetValue(ButtonTopProperty); }
set { SetValue(ButtonTopProperty, value); }
}
public MainWindowViewModel()
{
ButtonTop = 15;
}
}
}
First of all you need to set Binding Mode to TwoWay :
<Button Content="Move Button" Canvas.Top="{Binding ButtonTop, Mode=TwoWay}"
Click="ButtonBase_OnClick" />
Also, if you are setting it from code behind, set using SetCurrentValue() method otherwise binding will be broken and ViewModel instance won't be updated :
UIElement uiElement = (UIElement)sender;
uiElement.SetCurrentValue(Canvas.TopProperty, Canvas.GetTop(uiElement) + 5);
Like mentioned here , do not write code in wrapper properties of DP's:
The WPF binding engine calls
GetValue
andSetValue
directly (bypassing the property setters and getters).
If you need to synchronize on property change, create a PropertyChangedCallback and do synchronization over there:
public static readonly DependencyProperty ButtonTopProperty = DependencyProperty.
Register("ButtonTop", typeof(int), typeof(MainWindowViewModel),
new UIPropertyMetadata(ButtonTopPropertyChanged));
private static void ButtonTopPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs args)
{
// Write synchronization logic here
}
Otherwise simply have normal CLR property and you should consider implementing INotifyPropertyChanged on your class:
private double buttonTop;
public double ButtonTop
{
get { return buttonTop; }
set
{
if(buttonTop != value)
{
// Synchronize here
buttonTop = value;
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.