How to bind multiple user controls? - c#

I have created the following User Control:
<UserControl x:Class="TextBinder" ...>
<TextBox Text="{Binding ????}" />
</UserControl>
Now I am using my user control twice in my MainWindow. The MainWindow is then bound to my ViewModel (I set the DataContext). Now the problem is: how can I bind my user controls to the user_controlViewModel?
In my ViewModel, I have created two objects let's call them UC_1 and UC_2, they contain different texts and I would like to bind them to their respective user control in my MainWindow.
What should I put at ????
Note: please do not simplify mu TextBox to double textboxes in one usercontrol. This is not what I would like since in my real life example I have more stuff than textbox only and the usercontrol should be used multiple times in one view.
Thanks!

i gave you a general answer:
within a "real(a usercontrol you wanna use with different viewmodels with different property names)" usercontrol you bind just to your own DependencyProperties and you do that with ElementName or RelativeSource binding and you should never set the DataContext within a UserControl.
<UserControl x:Name="myRealUC" x:class="MyUserControl">
<TextBox Text="{Binding ElementName=myRealUC, Path=MyOwnDPIDeclaredInMyUc, Path=TwoWay}"/>
<UserControl>
if you do that you can easily use this Usercontrol in any view like:
<myControls:MyUserControl MyOwnDPIDeclaredInMyUc="{Binding MyPropertyInMyViewmodel}"/>
and for completeness: the Dependency Property
public readonly static DependencyProperty MyOwnDPIDeclaredInMyUcProperty = DependencyProperty.Register(
"MyOwnDPIDeclaredInMyUc", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
public bool MyOwnDPIDeclaredInMyUc
{
get { return (string)GetValue(MyOwnDPIDeclaredInMyUcProperty); }
set { SetValue(MyOwnDPIDeclaredInMyUcProperty, value); }
}

Thats right, you need yo declare a dependency property in your UserControl:
public partial class TextBinder:UserControl
{
public static readonly DependencyProperty textproperty =
DependencyProperty.Register("Text", typeof(string), typeof(TextBinder));
public string Text
{
get
{
return this.GetValue(textproperty) as string;
}
set
{
this.SetValue(textproperty,value);
}
}
}
And then, you can use your usercontrol in your window at this way:
<YourNamespace:TextBinder Text={Binding ViewModelProperty}/>

Related

How to write getters and setters to convert WPF dependency property? [duplicate]

I am trying (and failing) to do data binding on a dependency property in xaml. It works just fine when I use code behind, but not in xaml.
The user control is simply a TextBlock that bind to the dependency property:
<UserControl x:Class="WpfTest.MyControl" [...]>
<TextBlock Text="{Binding Test}" />
</UserControl>
And the dependency property is a simple string:
public static readonly DependencyProperty TestProperty
= DependencyProperty.Register("Test", typeof(string), typeof(MyControl), new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
I have a regular property with the usual implementation of INotifyPropertyChanged in the main window.
private string _myText = "default";
public string MyText
{
get { return _myText; }
set { _myText = value; NotifyPropertyChanged(); }
}
So far so good. If I bind this property to a TextBlock on the main window everything works just fine. The text update properly if the MyText changes and all is well in the world.
<TextBlock Text="{Binding MyText}" />
However, if I do the same thing on my user control, nothing happens.
<local:MyControl x:Name="TheControl" Test="{Binding MyText}" />
And now the fun part is that if I do the very same binding in code behind it works!
TheControl.SetBinding(MyControl.TestProperty, new Binding
{
Source = DataContext,
Path = new PropertyPath("MyText"),
Mode = BindingMode.TwoWay
});
Why is it not working in xaml?
The dependency property declaration must look like this:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(
nameof(Test),
typeof(string),
typeof(MyControl),
new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
The binding in the UserControl's XAML must set the control instance as the source object, e.g. by setting the Bindings's RelativeSource property:
<UserControl x:Class="WpfTest.MyControl" ...>
<TextBlock Text="{Binding Test,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
Also very important, never set the DataContext of a UserControl in its constructor. I'm sure there is something like
DataContext = this;
Remove it, as it effectively prevents inheriting a DataContext from the UserConrol's parent.
By setting Source = DataContext in the Binding in code behind you are explicitly setting a binding source, while in
<local:MyControl Test="{Binding MyText}" />
the binding source implicitly is the current DataContext. However, that DataContext has been set by the assignment in the UserControl's constructor to the UserControl itself, and is not the inherited DataContext (i.e. the view model instance) from the window.

WPF Bind User Control property not working

I have a user control and im trying to bind one of its properties
User Control Xaml
<UserControl x:Class="pi_browser.Testing.Example"
...
x: Name="LabelControl">
<StackPanel x:Name="RootStackPanel">
<Label Content="{Binding Text, ElementName=LabelControl}"/>
</StackPanel>
</UserControl>
User Control Codebehind
public partial class Example : UserControl
{
public Example()
{
InitializeComponent();
ExampleViewModel vm = new ExampleViewModel(State);
DataContext = vm;
}
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State",
typeof(Boolean), typeof(Example), new PropertyMetadata(false));
}
Main Page View Model
class ExampleViewModel
{
public ExampleViewModel(bool v)
{
val = v;
}
bool val;
public string Text { get => val ? "This worked" : "This didnt work"; }
}
Main Window Xaml
<Window x:Class="pi_browser.Testing.Tester" ... >
<Grid>
<local:Example State="True"/>
</Grid>
</Window>
In this example I didn't bind the State variable, I only passed a literal, but ideally I would like to bind to actual values.
State is a boolean, yet you bind to Text. Let us fix one issue by creating a dependency property Text on your User Control. We shall fix the Text issue and not the boolean State issue. Once you fix that, do the same for State.
So to fix Text we need to fix why this fails:
<Label Content="{Binding Text, ElementName=LabelControl}"/>
You set the ElementName to be the UserControl itself, which is what one wants. But then you tell the binding to look for (remember binding is just reflection of an object under the covers) the property Text. The property Text does not exist on that instance/class...but State does. Its obvious to bind to a newly created Text dependency property on the user control to fix the first issue.
Then when you instantiate the control on your main page, you need to then, and only then bind to Text because that property also resides on your viewmodel.
So three things, along with the change mentioned on the UserControl:
Make your ViewModel adhere to INotifyPropertyChanged and make the Text property use the notification mechanism you install.
Make sure that your main page has its DataContext set to a vailid instance of your ViewModel class.
Bind to Text such as <local:Example State="{Binding Text}"/>
Once that is done, the Text value will properly flow towards the UserControl.

Binding to property of UserControl doesn't work

I got a custom TextBox which I plan to include in another UserControl, however when setting up the Binding for it, it simply just doesn't bind.
I simplified the code for clarity.
My custom TextBox:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
partial class CustomTextBox : UserControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(String.Empty));
}
This binding works as expected. When using CustomTextBox in another UserControl or Window, I can access the property just as expected.
The following code blocks describe the UserControl that uses CustomTextBox and the corresponding ViewModel with the property I want to bind Text to.
<UserControl>
<UserControl.DataContext>
<vm:MyViewModel />
</UserControl.DataContext>
<local:CustomTextBox Text="{Binding FooBar, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
public class MyViewModel : INotifyPropertyChanged
{
private string _fooBar;
public string FooBar
{
get { return _fooBar = (_fooBar ?? ""); }
set
{
_fooBar = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My problem occurs exactly when I want to bind the Text property to a ViewModel in another UserControl, it just doesn't work. In this case I tried to bind the Text property to the FooBar property on the MyViewModel class, however changes to the Text property do not get reflected on the FooBar property and vice-versa. However when I hover over the binding in the XAML view, it shows the type of the property, so I don't exactly see what's wrong here.
My best guess is that it has to do with two bindings accessing the same property.
modify DP registration to include FrameworkPropertyMetadataOptions.BindsTwoWayByDefault option
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Show Validation Error Template on Controls within a UserControl in WPF

How do you get the WPF error template to appear on a control within a UserControl in WPF?
I have a UserControl containing two Labels, two TextBoxes, and a CheckBox. One of the TextBoxes represents the name of the entity and it is bound to a Name property off of a Model property exposed by my ViewModel, which is the DataContext of my Window. The Model class implements the IDataErrorInfo interface and I have confirmed through Unit Testing that when the Name is blank an error is returned through the property indexer implementation. I have bound to the Dependency Property backing the Name TextBox in my UserControl and when the validation error is encountered the WPF error template places a red border around the entire UserControl rather than just the Name TextBox.
The binding to the name field of the UserControl is as follows.
<vc:MyUserControl ItemName="{Binding Model.Name, ValidatesOnDataErrors=True}" />
A simiplified version of my UserControl and the backing DependencyProperty is as follows.
<UserControl>
<Grid>
<TextBox Text="{Binding ItemName}" />
</Grid>
</UserControl>
public partial class MyUserControl: UserControl
{
public static readonly DependencyProperty ItemNameProperty =
DependencyProperty.Register(
"ItemName",
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public string ItemName
{
get { return (string)GetValue(ItemNameProperty); }
set { SetValue(ItemNameProperty, value); }
}
}
The information I have found relating to this issue thus far has all been in regards to Silverlight or using a converter to not show the red border (which did not make sense to me). This information was all found here on stackoverflow.
Has anyone been able to solve this issue with WPF? Am I overlooking something obvious?
The ErrorTemplate for UserControl will be used if bindings to your UserControl use ValidatesOnDataErrors=True. But you can remove the red border with the Validation.ErrorTemplate Attached Property.
All controls within your UserControl will only show a red border if you validate their bindings by implementing IDataErrorInfo for the backing DependencyProperties too.
public class MyUserControl : UserControl, IDataErrorInfo
{
public static readonly DependencyProperty ItemNameProperty =
DependencyProperty.Register(
"ItemName",
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
public string ItemName
{
get { return (string)GetValue(ItemNameProperty); }
set { SetValue(ItemNameProperty, value); }
}
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string columnName]
{
get
{
// use a specific validation or ask for UserControl Validation Error
return Validation.GetHasError(this) ? "UserControl has Error" : null;
}
}
}
and here the simplified XAML
<UserControl Validation.ErrorTemplate="{x:Null}">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBox Text="{Binding ItemName, ValidatesOnDataErrors=True}" />
</Grid>
</UserControl>
Addition
If you want to differentiate between errors you can get the BindingExpression for your DependencyProperty and check the HasError Property.
BindingExpression be = BindingOperations.GetBindingExpression(this, ItemNameProperty);
return be != null && be.HasError ? "ItemName has Error" : null;

UserControl using parent elements in wpf?

When you have a usercontrol in wpf can it reach outside to its parent elements? For instance my user control just lists some generic things which are held inside the control which is encapsulated within a dockpanel on the main window, but I have a textbox and button in the main window that I would like to access from the control... is this possible?
It would save me alot of time rather than changing the content of the entire window and displaying the same textbox/button in every usercontrol. If anyone has an example of this it would be much appreciated.
Yes it is possible and here is some code I have used to compose presentations out of UserControls that have DPs.
I don't love it even a little, but it works. I also think this is a great topic and maybe some code will help get some better answers!
Cheers,
Berry
UserControl XAML
<Button x:Name="btnAddNewItem" Style="{StaticResource blueButtonStyle}" >
<StackPanel Orientation="Horizontal">
<Image Source="{resx:Resx ResxName=Core.Presentation.Resources.MasterDetail, Key=bullet_add}" Stretch="Uniform" />
<Label x:Name="tbItemName" Margin="5" Foreground="White" Padding="10, 0">_Add New [item]</Label>
</StackPanel>
</Button>
UserControl Code Behind
public partial class AddNewItemButton : UserControl
{
...
#region Item Name
public static readonly DependencyProperty ItemNameProperty = DependencyProperty.Register(
"ItemName", typeof(string), typeof(AddNewItemButton),
new FrameworkPropertyMetadata(OnItemNameChanged));
public string ItemName
{
get { return (string)GetValue(ItemNameProperty); }
set { SetValue(ItemNameProperty, value); }
}
public string ButtonText { get { return (string) tbItemName.Content; } }
private static void OnItemNameChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
// When the item name changes, set the text of the item name
var control = (AddNewItemButton)obj;
control.tbItemName.Content = string.Format(GlobalCommandStrings.Subject_Add, control.ItemName.Capitalize());
control.ToolTip = string.Format(GlobalCommandStrings.Subject_Add_ToolTip, control.ItemName);
}
#endregion
#region Command
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(AddNewItemButton),
new FrameworkPropertyMetadata(OnCommandChanged));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
private static void OnCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
// When the item name changes, set the text of the item name
var control = (AddNewItemButton)obj;
control.btnAddNewItem.Command = control.Command;
}
#endregion
}
Another UserControl showing Composition
<UserControl ...
xmlns:uc="clr-namespace:Smack.Core.Presentation.Wpf.Controls.UserControls"
>
<DockPanel LastChildFill="True">
...
<uc:AddNewItemButton x:Name="_addNewItemButton" Margin="0,0,10 0" DockPanel.Dock="Right" />
...
</DockPanel>
</UserControl>
A better design pattern would be to have the usercontrol notify (via event) the main window when something needs to be changed, and to ask the window (via method) when it needs some information. You would, for example, have a GetText() method on the window that the usercontrol could call, and a ChangeText event on the usercontrol that the window would subscribe to.
The idea is to keep the window in control at all times. Using this mentality will make it easier for you to develop applications in the future.
To answer your question: yes, you can either access parent controls either through a RelativeSource binding or through the Parent member in the back code. But a better answer is similar to #KendallFrey answer. Adopt a framework like Light MVVM and use its messenger class or use events the way Kendall described.

Categories

Resources