I have a WPF-Application with a Window and a UserControl. The UserControl is implemented with the MVVM-Pattern. So in the view I have a Label which displays the value of a string-property called InfoMessage in the ViewModel.
In the Window I added an instance of this UserControl by
<views:ItemInfoView Grid.Row="3" Grid.Column="1" x:Name="itemInfoView"/>
Now I want to set the InfoMessage from the XAML of my Window. Currently I have no idea how to achieve this in xaml. In code-behind I could access the DataContext of my control and cast it to ItemInfoViewModel and the set the value like:
((ItemInfoViewModel)itemInfoView.DataContext).InfoMessage = "Hello World";
But I hope there's a way to do this in pure XAML. Does anyone know if this is possible and how?
You need to add a Dependency Property to your user control:
// Your property
public string InfoMessage{get;set;}
// Register Dependency Property
public static readonly DependencyProperty InfoMessageProperty =
DependencyProperty.Register("InfoMessage", typeof(string), typeof(ItemInfoView),
new UIPropertyMetadata(true));
Then you should be able to just set or bind InfoMessage directly:
<views:ItemInfoView Grid.Row="3" Grid.Column="1" InfoMessage="Whatever"/>
Related
This question already has an answer here:
How to pass data from MainWindow to a User Control that's inside the MainWindow?
(1 answer)
Closed 2 years ago.
I have a custom control that I created to test how binding works. It has a dependency property that I want to bind from another view and also I want it to update when binded property raises PropertyChanged using INotifyPropertyChanged:
public string BindLabelText
{
get { return (string)GetValue(BindLabelTextProperty); }
set { SetValue(BindLabelTextProperty, value); }
}
public static readonly DependencyProperty BindLabelTextProperty =
DependencyProperty.Register("BindLabelText", typeof(string), typeof(BindingControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
In my control I bind dependency property like this:
<TextBlock
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Text="{Binding ElementName=TestControl, Path=BindLabelText, FallbackValue=BindLabelText}"
FontSize="20"/>
And I use control like this:
<local:BindingControl BindLabelText="Static main window value" />
<local:BindingControl BindLabelText="{Binding HeaderLabel, FallbackValue=HeaderLabel}" />
<Button Content="Change MainWindow property" Command="{Binding ChangeMainTextCommand}"/>
TextBlock text value is set with a static value but it does not work when I try to bind it to a property. In both views I set DataContext to ViewModel in code behind:
this.DataContext = new BindingControlViewModel();
Edit:
Added my code to github. Like I said in the comments I'm trying to bind property on MainWindowViewModel which is the DataContext for my parent View (MainWindow) to my User Control DP BindLabelText. User Control in addition to DP also has its own ViewModel. My goal is to have User Control that will update when bound property from MainWindowViewModel updates.
I managed to solve it by setting my control DataContext to grid like MyGrid.DataContext = new BindingControlViewModel(); instead of setting it on the control itself like this.DataContext = new BindingControlViewModel();.
So, i made a "custom textbox". The UserControl is just a Grid with a Rectangle and a TextBox, some code behind just colores the rectangle if the mouse is over and colores the text of the TextBox is it's keyboard focussed. I want to have acces to all the properties a TextBox has without having to manualy wire every one up by setting dependency properties and then binding them up in XAML. Is there any way to just let the TextBox properties to be accesible fron XAML?
Yes, you can do it. Give your TextBox in UserControl name:
<TextBox x:Name="uscTbx" Text={Binding toSomething}/>
In code behind of your UserControl define a property:
public TextBox UscTexBox { get { return uscTbx; }}
In XAML of another windows you can acces the properties of your TextBox:
<local:UserControl1 x:Name="usc1"/>
<TextBox Text="{Binding Path=UscTexBox.Text, ElementName=usc1}"/>
I have created a very simple user control that shows a ColorPicker (from the WPF Extended Toolkit) and a text field for its hex code:
<UserControl x:Class="HexColorPicker"> <!-- namespace declarations omitted -->
<UserControl.Resources>
<glue:ColorToRgbHex x:Key="colorToHex"/> <!-- custom converter I made -->
</UserControl.Resources>
<StackPanel Orientation="Horizontal" Name="layoutRoot">
<Label Content="#"/>
<TextBox Text="{Binding SelectedColor, Converter={StaticResource colorToHex}}"/>
<extToolkit:ColorPicker SelectedColor="{Binding SelectedColor}"/>
</StackPanel>
</UserControl>
And here is the backing code:
public partial class HexColorPicker : UserControl
{
public static readonly DependencyProperty SelectedColorProperty
= DependencyProperty.Register("SelectedColor", typeof(Color), typeof(HexColorPicker));
public HexColorPicker()
{
InitializeComponent();
layoutRoot.DataContext = this;
}
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
}
The layoutRoot.DataContext shenanigans come from this place I found.
I then use my control like that:
<me:HexColorPicker SelectedColor="{Binding MyColor}"/>
And it somewhat works. The text field and the color picker are in sync: when one changes, the other changes as well. However, the control and the model object aren't two-way synced: my control will update if I change the model object's MyColor property, but the MyColor property will not update when I change it with my control.
What am I doing wrong? Why is the binding one-way from my model to my control only?
Change your DependencyProperty Declaration to:
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof (Color), typeof (HexColorPicker), new FrameworkPropertyMetadata(default(Color),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
As I recall, bindings that convert between types sometimes default to OneWay binding.
From the reference for BindingMode.Default (http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx):
Uses the default Mode value of the binding target. The default value varies for each dependency property. In general, user-editable control properties, such as those of text boxes and check boxes, default to two-way bindings, whereas most other properties default to one-way bindings. A programmatic way to determine whether a dependency property binds one-way or two-way by default is to get the property metadata of the property using GetMetadata and then check the Boolean value of the BindsTwoWayByDefault property.
It looks like the problem is that your control isn't seen as a 'user-editable' control.
The easiest solution is to specify Mode=TwoWay in your binding.
Assume this situation:
I have created a new control ("MyControl") with DependencyProperty "SuperValue".
Now, in XAML i set "SuperValue" to "TestValue":
<local:MyControl SuperValue="TestValue" />
This control has a ViewModel (DataContext).
I want to pass value of DependencyProperty (in this example "TestValue") to property in ViewModel.
How can I do this?
Assume that ViewModel of my control do something calculations, for example: User inputs name of country, and control give him a time which is currently there.
The problem is: How can I provide the result of calculation? Assume that this is public property "Results" in ViewModel. I want to create a property like "TextBox.Text", "ListView.SelectedItem" which provides a part of ViewModel data "to outside".
For example TextBox and Text property:
<TextBox Text={Binding GiveMeTextValue} />
In this case DP "Text" provides to outside a ViewModel property which currently stores inputted text.
I want to use my control in the same way.
I don't know whether I get your question right: You want to set a static non-bound value in XAML to a DependencyProperty of the control and set a property on the control's DataContext to this static value? There is something wrong about your concept if you need to do this, why don't you provide this value on the ViewModel in an according field and bind the DP of the control to this field?
However, what you can do get what you want:
Define a PropertyChangedCallback when you register the DP:
// Dependency Property
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(string),
typeof(MyControl), new FrameworkPropertyMetadata("123", new PropertyChangedCallback(OnTestChanged)));
In the OnTestChanged method, cast your DataContext to the type of your ViewModel and set the according value on the ViewModel to the new value:
private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl c = d as MyControl;
ViewModelType vm = c.DataContext as ViewModelType;
vm.Property = e.New;
Console.WriteLine(e.NewValue);
}
Is that what you're asking for?
What about setting the MyDependencyProperty from the setter of property SomethingValueInDataContext.
EDIT
You can set the controls DependencyProperty where the control is used and not on its declaration. This will work (local is namespace where control resides) -
<Grid>
<local:MyOwnControl MyDependencyProperty="{Binding Test}"/>
</Grid>
Same as like you can set the Width of the TextBox when you create an instance of it in xaml like this-
<TextBox Width="{Binding PropertyName}"/>
Notice, the root of your xaml is UserControl and not MyOwnControl. UserControl is the base class of MyOwnControl; your property is not defined in the base class. This is why you cannot reference MyDependencyProperty from within the root element of the UserControl.
Using your example, you can switch the binding and get your desired effect.
<UserControl
x:Class="namespace.MyOwnControl"
x:Name="root">
<UserControl.DataContext>
<local:ControlViewModel
Test={Binding MyDependencyProperty, ElementName=root}" />
</UserControl.DataContext>
</UserControl>
Since you are using a MVVM design paradigm all data should be relative to the ViewModel. So your DP should be set via the binding in your VM property.
If the test data is going to be used in Blend/VS designer you can check for that vs. Debug/Release... then do some sort of assignment to your property based off of that check for testing.
You could add a property to MyControl called InitialSuperValue that when set, sets the value of SuperValue. Then write some XAML like this:
<local:MyControl InitialSuperValue="TestValue" SuperValue="{Binding SuperValueInViewModel, Mode=OneWayToSource}" />
I have a window which has a usercontrol in it . This usercontrol's RequestObject property bound to SearchArgumentObject property of ViewModel of the window.
This is listing from my window class
<Grid DataContext="{Binding SearchArgumentObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<guiLib:RegCardSearchForm x:Name="SearchParametrsUC" Grid.Row="1" RequestObject="{Binding .,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
In Usercontrol class I created dependency property:
This is listing from my userControl class
public static DependencyProperty RequestObjectProperty = DependencyProperty.Register("RequestObject", typeof(RegistrationCardSearch), typeof(RegCardSearchForm));
public RegistrationCardSearch RequestObject
{
get
{
return (RegistrationCardSearch)GetValue(RequestObjectProperty);
}
set
{
SetValue(RequestObjectProperty, value);
}
}
On the level of the usecontrol everything works fine and RequestOject property changed.
But in my window class I can't see modification of SearchArgumentObject property which was made in usercontrol.
How can I get modefied property value? I think answer to this question is very trivial but I can't find solution.
Setting the DataContext on the Grid isn't doing anything but breaking the two-way linking of your properties. Skip the extra step and bind the VM property to the control property that you want to pick up changes from instead:
<Grid>
<guiLib:RegCardSearchForm x:Name="SearchParametrsUC" Grid.Row="1"
RequestObject="{Binding SearchArgumentObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
The code for your Window class is setting the DataContext of the Grid to a property obtained from a binding to a property on another object's DataContext further up the tree. Do you have the Window's DataContext set elsewhere?
Let's say that the object which is supplying the SearchArgumentObject is named SearchWindowViewModel. In the code-behind of the Window, you would have the following code (in the constructor, for example):
DataContext = new SearchWindowViewModel();
Now, all the properties that SearchWindowViewModel exposes are available to the Window. To bind the SearchWindowViewModel.SearchArgumentObject to the UserControl's RequestObject property, you would have the following XAML:
<Grid>
<guiLib:RegCardSearchForm x:Name=SearchParametersUC Grid.Row=1
RequestObject={Binding SearchArgumentObject />
</Grid>
If you don't want to set the Window's DataContext, you can set the Grid's DataContext using the same type of code as I used above, and the binding in the XAML would remain the same.
Hope that helps.