MainWindow.XAML:
<Window x:Class="TestWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="myMainWindow"
Title="MainWindow" Width="200" Height="250">
<Grid Name="MainGrid" SizeChanged="MainGrid_SizeChanged">
<Grid.LayoutTransform>
<ScaleTransform x:Name="ApplicationScaleTransform"
CenterX="0"
CenterY="0"
ScaleX="{Binding ElementName=myMainWindow, Path=ScaleValue}"
ScaleY="{Binding ElementName=myMainWindow, Path=ScaleValue}" />
</Grid.LayoutTransform>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Height="150">
<TextBlock FontSize="20" Text="Hello World" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Button Content="Button" VerticalAlignment="Bottom" HorizontalAlignment="Center" Name="myButton"/>
</Grid>
</Grid>
</Window>
Main Window CodeBehind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
#region ScaleValue Depdency Property
public static readonly DependencyProperty ScaleValueProperty = DependencyProperty.Register("ScaleValue", typeof(double), typeof(MainWindow), new UIPropertyMetadata(1.0, new PropertyChangedCallback(OnScaleValueChanged), new CoerceValueCallback(OnCoerceScaleValue)));
private static object OnCoerceScaleValue(DependencyObject o, object value)
{
MainWindow mainWindow = o as MainWindow;
if (mainWindow != null)
return mainWindow.OnCoerceScaleValue((double)value);
else
return value;
}
private static void OnScaleValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
MainWindow mainWindow = o as MainWindow;
if (mainWindow != null)
mainWindow.OnScaleValueChanged((double)e.OldValue, (double)e.NewValue);
}
protected virtual double OnCoerceScaleValue(double value)
{
if (double.IsNaN(value))
return 1.0f;
value = Math.Max(0.1, value);
return value;
}
protected virtual void OnScaleValueChanged(double oldValue, double newValue)
{
}
public double ScaleValue
{
get
{
return (double)GetValue(ScaleValueProperty);
}
set
{
SetValue(ScaleValueProperty, value);
}
}
#endregion
private void MainGrid_SizeChanged(object sender, EventArgs e)
{
CalculateScale();
}
private void CalculateScale()
{
double yScale = ActualHeight / 250f;
double xScale = ActualWidth / 200f;
double value = Math.Min(xScale, yScale);
ScaleValue = (double)OnCoerceScaleValue(MainGrid, value);
}
}
I have this sample application. I am applying LayoutTransform on the MainGrid to scale application with the resize of window. How can i avoid my control myButton to apply that transform? I dont want that transform to apply on it.
Two options
1) Don't make it a child, instead only make it visually overlap, using a second grid for example.
2) Apply the inverse of the transformation to the children, this will negate the transformation, can be expensive but should do the trick.
<Button LayoutTransform="{Binding ElementName=MainGrid, Path=LayoutTransform.Inverse}"/>
personally i would use the first option.
Related
I have code below inside Grid:
<Grid.RenderTransform>
<TranslateTransform
X="{Binding X, Converter={StaticResource HorizontalPositionConverter}}"
Y="{Binding Y, Converter={StaticResource VerticalPositionConverter}}"
/>
</Grid.RenderTransform>
How can I get binding of TranslateTransform.X or TranslateTransform.Y in code behind? I found this question but solution works for non-nested dependency properties. What to do when they are? I cannot consider binding to entire RenderTransform. I am developing winrt app, so multibinding is out of the game.
Here is the code behind binding. I didn't use a converter because my X and Y are defined double. In order to make a correct binding you have to use dependecy property or another notification mechanism (like INotifyPropertyChanged implementation). Here is code behind binding solution (not MVVM). I've added the button to test the moving.
1. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.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" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RenderTransform>
<TranslateTransform
X="{Binding ElementName=This, Path=X, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Y="{Binding ElementName=This, Path=Y, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid.RenderTransform>
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
2. Code behind :
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
}
Update 1:
Here is code-behind based solution, there is no binding in XAML:
3. Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (MainWindow), new PropertyMetadata(default(double)));
private static double _position;
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
X = ++_position;
Y = _position;
}
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
if(grid == null) return;
var transform = grid.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = InitTransformBinding();
grid.RenderTransform = transform;
}
else
{
InitTransformBinding(transform);
}
}
private TranslateTransform InitTransformBinding(TranslateTransform t = null)
{
var transform = t ?? new TranslateTransform();
var xBinding = new Binding();
xBinding.Source = this;
xBinding.Path = new PropertyPath("X");
xBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
xBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.XProperty, xBinding);
var yBinding = new Binding();
yBinding.Source = this;
yBinding.Path = new PropertyPath("Y");
yBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
yBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(transform, TranslateTransform.YProperty, yBinding);
return transform;
}
}
4. XAML code:
<Window x:Class="TransformBindingSoHelpAttempt.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" x:Name="This">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Loaded="FrameworkElement_OnLoaded">
<Button Content="Click" Width="100" Height="100" Click="ButtonBase_OnClick"></Button>
</Grid>
Update 2, here on each button click you will scale the grid.
5. Xaml code:
Window x:Class="TransformBindingSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:transformBindingSoHelpAttempt="clr-namespace:TransformBindingSoHelpAttempt"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<transformBindingSoHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type transformBindingSoHelpAttempt:ItemDataContext}">
<Grid>
<Grid.RenderTransform>
<ScaleTransform
ScaleX="{Binding X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScaleY="{Binding Y, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid.RenderTransform>
<Button Content="{Binding ButtonContent}" Command="{Binding ButtonCommand}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
6. View models:
public class MainViewModel:BaseObservableObject
{
public MainViewModel()
{
Items = new ObservableCollection<ItemDataContext>(new List<ItemDataContext>
{
new ItemDataContext{ButtonContent = "A", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "B", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "C", X = 1.0, Y = 1.0},
new ItemDataContext{ButtonContent = "D", X = 1.0, Y = 1.0},
});
}
public ObservableCollection<ItemDataContext> Items { get; set; }
}
public class ItemDataContext:BaseObservableObject
{
private ICommand _buttonCommand;
private object _buttonContent;
private double _x;
private double _y;
public double X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged();
}
}
public double Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged();
}
}
public ICommand ButtonCommand
{
get { return _buttonCommand ?? (_buttonCommand = new DelegateCommand(Target)); }
}
public object ButtonContent
{
get { return _buttonContent; }
set
{
_buttonContent = value;
OnPropertyChanged();
}
}
private void Target(object obj)
{
X += 0.2;
Y += 0.2;
}
}
7. How it is looks like:
Please keep in mind that the last update solution is based on LayouTransform and re-build the view on each button click (makes it to be scaled).
Regards,
From MVVM Design pattern, the viewmodel should not know the view. But in my case, I need the view and the model, I mean :
In my window, I've an Image component. I'd like to get mouse position when mouse moves over the Image component and save it into my model.
The code behind would have been :
void Foo_MouseMove(objet sender, MouseEventArgs e)
{
model.x = e.getPosition(this.imageBox).X;
model.y = e.getPosition(this.imageBox).Y;
}
The problem is : I need this.imageBox and MouseEventArgs, so two View element.
My question is : How to deal with this case using the MVVM approach ?
I use MVVM light framework
I would use an attached behaviour here. This will allow you to continuously monitor the mouse position, rather than simply responding to an event such as MouseDown. You'll need to add a reference to the System.Windows.Interactivity assembly.
The code below provides a simple example of this in action.
XAML
<Window x:Class="MouseMoveMvvm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mouseMoveMvvm="clr-namespace:MouseMoveMvvm"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock Text="{Binding PanelX, StringFormat='X={0}'}" />
<TextBlock Text="{Binding PanelY, StringFormat='y={0}'}" />
</StackPanel>
<Canvas DockPanel.Dock="Bottom" Background="Aqua">
<i:Interaction.Behaviors>
<mouseMoveMvvm:MouseBehaviour MouseX="{Binding PanelX, Mode=OneWayToSource}" MouseY="{Binding PanelY, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</Canvas>
</DockPanel>
</Grid>
</Window>
Note that, in the above XAML, the MouseBehaviour is pushing the mouse position down to the ViewModel through a OneWayToSource binding, while the two TextBlocks are reading the mouse positions from the ViewModel.
ViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
private double _panelX;
private double _panelY;
public event PropertyChangedEventHandler PropertyChanged;
public double PanelX
{
get { return _panelX; }
set
{
if (value.Equals(_panelX)) return;
_panelX = value;
OnPropertyChanged();
}
}
public double PanelY
{
get { return _panelY; }
set
{
if (value.Equals(_panelY)) return;
_panelY = value;
OnPropertyChanged();
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Attached Behaviour
public class MouseBehaviour : System.Windows.Interactivity.Behavior<FrameworkElement>
{
public static readonly DependencyProperty MouseYProperty = DependencyProperty.Register(
"MouseY", typeof (double), typeof (MouseBehaviour), new PropertyMetadata(default(double)));
public double MouseY
{
get { return (double) GetValue(MouseYProperty); }
set { SetValue(MouseYProperty, value); }
}
public static readonly DependencyProperty MouseXProperty = DependencyProperty.Register(
"MouseX", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));
public double MouseX
{
get { return (double) GetValue(MouseXProperty); }
set { SetValue(MouseXProperty, value); }
}
protected override void OnAttached()
{
AssociatedObject.MouseMove += AssociatedObjectOnMouseMove;
}
private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
var pos = mouseEventArgs.GetPosition(AssociatedObject);
MouseX = pos.X;
MouseY = pos.Y;
}
protected override void OnDetaching()
{
AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove;
}
}
Finnally found an answer, using a EventConverter :
public class MouseButtonEventArgsToPointConverter : IEventArgsConverter
{
public object Convert(object value, object parameter)
{
var args = (MouseEventArgs)value;
var element = (FrameworkElement)parameter;
var point = args.GetPosition(element);
return point;
}
}
This converter allows me to deal with Point and not with graphics components.
Here goes the XML :
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<cmd:EventToCommand
Command="{Binding Main.MouseMoveCommand, Mode=OneWay}"
EventArgsConverter="{StaticResource MouseButtonEventArgsToPointConverter}"
EventArgsConverterParameter="{Binding ElementName=Image1}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Mark Greens solution is the best (I found).
If you want to make his solution reusable for any WPF control (which I suggest), inheriting from System.Windows.Interactivity.Behavior<Control> actually won't work for Panel, because Panel does not inherit from Control.
Only those classes inherit from Control:
https://msdn.microsoft.com/de-de/library/system.windows.controls.control(v=vs.110).aspx
Instead, inherit from System.Windows.Interactivity.Behavior<FrameworkElement>. FrameworkElement is the ancestor of all WPF control classes: https://msdn.microsoft.com/de-de/library/system.windows.frameworkelement(v=vs.110).aspx.
I have tested it on Grid, Panel and Image btw.
I use it to keep a Popup in sync with the mouse cursor:
<Image x:Name="Image1">
<i:Interaction.Behaviors>
<myNamespace:MouseBehaviour
MouseX="{Binding ElementName=Popup1, Path=HorizontalOffset, Mode=OneWayToSource}"
MouseY="{Binding ElementName=Popup1, Path=VerticalOffset, Mode=OneWayToSource}">
</myNamespace:MouseBehaviour>
</i:Interaction.Behaviors>
</Image>
<Popup x:Name="Popup1" PlacementTarget="{Binding ElementName=Image1}"/>
P.S.: I would have commented on the solution, but my answer is too long.
I have a user control that contains 2 DoubleUpDown, I have bound point to that controls
<DoubleUpDown x:Name="X" Grid.Column="1" Grid.Row="0" Value="{Binding Path=Value.X, Mode=TwoWay" />
<DoubleUpDown x:Name="Y" Grid.Column="1" Grid.Row="1" Value="{Binding Path=Value.Y, Mode=TwoWay}" />
controls get updated pretty well when I change Value from outside, but Value stays unchanged when I change controls data.
I bound Value to user control from code inside
Point2DEditorView editor = new Point2DEditorView();
Binding binding = new Binding("Value");
binding.Mode = BindingMode.TwoWay;
editor.SetBinding(Point2DEditorView.ValueProperty, binding);
and Point2DEditorView.Value also changed when I insert new coordinates into controls. But that does not affect bound Value.
Point is a value type data. Because of this when you bind it to control boxing and unboxing occurs. For more information see this. So, you may easy solve this problem by creating your own class (not struct!):
class MyPoint
{
public int X { set; get; }
public int Y { set; get; }
}
And then bind this objects to your control and you will see that all works as you expect.
Update
First of all your DoubleUpDown is'n in standart FCL and I think your problem in it. There is a simple example where all works as expect. I created a simple UpDown control for it:
Point class
public class Point2D : INotifyPropertyChanged
{
private double x;
private double y;
public double X
{
set
{
if (value.Equals(x)) return;
x = value;
OnPropertyChanged();
}
get { return x; }
}
public double Y
{
set
{
if (value.Equals(y)) return;
y = value;
OnPropertyChanged();
}
get { return y; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
UpDown xaml
<UserControl x:Name="doubleUpDown" x:Class="PointBind.DoubleUpDown"
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:DesignWidth="105" Height="33">
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=doubleUpDown}">
<TextBox Margin="5,5,0,5" Width="50" Text="{Binding Value}" />
<Button x:Name="Up" x:FieldModifier="private" Margin="5,5,0,5" Content="˄" Width="20" Click="Up_Click" />
<Button x:Name="Down" x:FieldModifier="private" Margin="0,5,0,5" Content="˅" Width="20" Click="Down_Click" />
</StackPanel>
</UserControl>
UpDown .cs
public partial class DoubleUpDown : UserControl
{
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(DoubleUpDown), new PropertyMetadata(0.0));
public DoubleUpDown()
{
InitializeComponent();
DataContext = this;
}
private void Up_Click(object sender, RoutedEventArgs e)
{
Value++;
}
private void Down_Click(object sender, RoutedEventArgs e)
{
Value--;
}
}
Point2DEditorView xaml
<UserControl x:Name="point2DEditorView" x:Class="PointBind.Point2DEditorView"
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"
xmlns:local="clr-namespace:PointBind"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<local:DoubleUpDown Value="{Binding Point.X, ElementName=point2DEditorView, Mode=TwoWay}"/>
<local:DoubleUpDown Value="{Binding Point.Y, ElementName=point2DEditorView, Mode=TwoWay}"/>
</StackPanel>
</UserControl>
UpDown .cs
public partial class Point2DEditorView : UserControl
{
public Point2D Point
{
get { return (Point2D)GetValue(PointProperty); }
set { SetValue(PointProperty, value); }
}
// Using a DependencyProperty as the backing store for Point. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointProperty =
DependencyProperty.Register("Point", typeof (Point2D), typeof (Point2DEditorView),
new PropertyMetadata(new Point2D {X = 10, Y = 20}));
public Point2DEditorView()
{
InitializeComponent();
}
}
Test form xaml
<Window x:Class="PointBind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PointBind"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Point2DEditorView x:Name="pointEditor"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="39,121,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
And test form .cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
pointEditor.Point = new Point2D{X = 300, Y = 400};
}
}
Hope this helps.
How could I make possible to access data/properties from a UserControl and the parent, MainWindow (and viceversa)? For example, say I have a TextBox in my MainWindow called mainTextBox. Then I create a UserControl with another TextBox called ucTextBox. I also have a button called ucButton in the UserControl that should popup a MessageBox with the product of the values mainTextBox.Text * ucTextBox.Text (converted to double, to make it work).
What I really want to know is how to achieve to do this dynamically, with a button that allows to create more UserControls that are capable to interact with the parent. In this case it makes no sense to name every UserControl.
I've tried several things, mainly with get,set properties but with no desired outcome.
I'm not sure if I need to use UserControl, but it seems to, I've read that CustomControl is for a deep customization but I don't need that.
Here is just a quick sample to get you started (and what probably was meant by mr. #Adriano):
RootViewModel.cs:
public class RootViewModel :INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate {};
private void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private double _x;
private double _y;
public double X
{
get { return _x; }
set
{
_x = value;
OnPropertyChanged("X");
}
}
public double Y
{
get { return _y; }
set
{
_y = value;
OnPropertyChanged("Y");
}
}
public double XY
{
get { return _x * _y; }
}
}
UserControl1.xaml:
<UserControl x:Class="WpfApplication2.UserControl1"
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:DesignWidth="200">
<Grid>
<GroupBox Header="User Control">
<StackPanel>
<Label Content="Y:" />
<TextBox Text="{Binding Path=Y, UpdateSourceTrigger=PropertyChanged, FallbackValue=1}" Margin="5" />
<Button Content="Press me" Click="OnButtonClick" />
</StackPanel>
</GroupBox>
</Grid>
UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var viewModel = (RootViewModel)DataContext;
var resultMessage = string.Format("{0} * {1} = {2}", viewModel.X, viewModel.Y, viewModel.XY);
MessageBox.Show(resultMessage, "X * Y");
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication21="clr-namespace:WpfApplication2"
Title="Main Window" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel>
<Label Content="X:" />
<TextBox Text="{Binding Path=X, UpdateSourceTrigger=PropertyChanged, FallbackValue=1}" Margin="5" Height="24" />
</StackPanel>
<WpfApplication21:UserControl1 Grid.Row="1" Margin="5" />
</Grid>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new RootViewModel
{
X = 5,
Y = 7
};
}
}
My UserControl contains a TextBox and a Button. The TextBox's Text is correctly populated by a dependency property called X.
My Goal:
Change the value of X (e.g. Text of the TextBox) when I press the Button.
I have defined the UserControl as follows:
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Text="{Binding Path=X}" Width="50"/>
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
With codebehind:
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(MyUserControl), new PropertyMetadata(0.0));
private void InsertCurrentBtnClick(object sender, RoutedEventArgs e)
{
X = 0.7;
//BindingOperations.GetBindingExpression(this, XProperty).UpdateTarget();
//BindingOperations.GetBindingExpression(Xbox, TextBox.TextProperty).UpdateTarget();
//BindingOperations.GetBindingExpression(Xbox, XProperty).UpdateTarget();
//Xbox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
//GetBindingExpression(XProperty).UpdateTarget();
}
I have tried several things - one at a time - (see below X=0.7;) to force the update to the TextBox Text but nothing has helped so far.
Thanks in advance.
I'd write it in this way:
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(MainPage), new PropertyMetadata(new PropertyChangedCallback(Callback)));
public static void Callback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MainPage).Xbox.Text = e.NewValue.ToString();
}
private void InsertCurrentBtnClick(object sender, RoutedEventArgs e)
{
X = 0.7;
}
And the xaml code:
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Width="50"/>
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
You need to set the DataContext for you Control. As I see X defined in your control, you need to do this :
public MyUserControl()
{
InitializeComponent();
// add this line
this.DataContext = this;
}
Although, you can bind it as well, just change the xaml:
<UserControl x:Class="SilverlightApplication1.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"
Name="myWidnow"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal" >
<TextBox Name="Xbox" Width="50" Text="{Binding ElementName=myWidnow, Path=X}" />
<Button Content="Current" Click="InsertCurrentBtnClick" />
</StackPanel>
</Grid>
Notice that I've added the Name proeprty to the UserControl.
In this case, you don't have to change anything in the code behid.