ItemsSource Binding in UserControl doens't work [duplicate] - c#

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
XAML binding not working on dependency property?
(1 answer)
Closed 3 years ago.
i am currently testing UserControls and therefore created this little App.
Main.xaml
<Grid>
<control:CustomInterfaceGrid Color="Green" Height="400" CustomItemsSource="{Binding Packages}"></control:CustomInterfaceGrid>
</Grid>
UserControl.xaml
<UserControl x:Class="App.Custom.CustomInterfaceGrid"
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"
xmlns:local="App.Custom"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="SourceElement"
>
<Grid>
<ListBox x:Name="listView" Background="{Binding Color, ElementName=SourceElement}" ItemsSource="{Binding CustomItemsSource, ElementName=SourceElement}"></ListBox>
</Grid>
</UserControl>
CodeBehind from UserControl
public partial class CustomInterfaceGrid : UserControl, INotifyPropertyChanged
{
public CustomInterfaceGrid()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(SolidColorBrush), typeof(CustomInterfaceGrid));
public SolidColorBrush Color
{
get; set;
}
public static readonly DependencyProperty CustomItemsSourceProperty =
DependencyProperty.Register("CustomItemsSource", typeof(IEnumerable<Object>), typeof(CustomInterfaceGrid));
public IEnumerable<Object> CustomItemsSource
{
get
{
return GetValue(CustomItemsSourceProperty) as IEnumerable<Object>;
}
set {
SetValue(CustomItemsSourceProperty, value);
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The problem is that my the color changes when I set it in Main, but it wont show the Packages from the List.
When i bind the Packages directly to a list in Main.xaml its alright. So the fault has to be somewhere else.
Hope you can help!

the main mistakes which leads to binding erros is unnecessary set DataContext. remove this line from constructor:
DataContext = this;
there is no need to implement INotifyPropertyChanged for UserControl which is also DependencyObject. DependencyProperties have internal mechanism to notify about changes. Remove OnPropertyChanged - declaration and all usages.
Speaking of DependencyProperties: public SolidColorBrush Color { get; set; } doesn't follow required pattern and has to be implemented with GetValue / SetValue method

Related

Trivial binding to property of a UserControl - not working yet no errors [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
How to correctly bind to a dependency property of a usercontrol in a MVVM framework
(4 answers)
Closed 4 years ago.
I am attempting to get to grips with DataBinding, and I have just started to introduce UserControls into the mix, but it appears I cannot even get the most basic example working.
I have a MainWindow that contains a property (Subtitle) along with an instance of a UserControl. A property of the UserControl (LabelContent) is bound to the Subtitle property. The expected result is that changing the Subtitle property of MainWindow will update the LabelContent property of the UserControl, which is bound to the actual Content property of a Label within the UserControl:
I have MainWindow.xaml:
<Window x:Class="WpfApp1.MainWindow"
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"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<local:UserControlSample LabelContent="{Binding Subtitle}"/>
With the following code behind (MainWindow.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
Subtitle = "Test";
}
public String Subtitle
{
get;
set;
}
}
UserControlSample.xaml:
<UserControl x:Class="WpfApp1.UserControlSample"
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"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Label Content="{Binding Path=LabelContent}"/>
UserControl.cs:
public partial class UserControlSample : UserControl
{
public static DependencyProperty LabelContentProperty = DependencyProperty.Register("LabelContent", typeof(String), typeof(UserControlSample));
public UserControlSample()
{
InitializeComponent();
DataContext = this;
}
public String LabelContent
{
get { return (String)GetValue(LabelContentProperty); }
set { SetValue(LabelContentProperty, value); }
}
}
I believe it is something to do with DataContexts however I do not understand them too well as yet. If I change UserControl to the following, then I achieve the expected results. However this does not explain the reasons why the original code fails:
UserControlSample.xaml
<Label Content="{Binding Path=Subtitle}"/>
UserControl.cs
public UserControlSample()
{
InitializeComponent();
}
You would need changes at two places,
First in Xaml, you need to set the source object of the Binding to the UserControl
<Label Content="{Binding Path=LabelContent,RelativeSource={RelativeSource AncestorType=UserControl}}" />
Secondly,
public UserControl1()
{
InitializeComponent();
}
That should allow you to bind the User Control correctly

Change The Binding Source To Another CLR Object

This is a mostly out of curiosity question and to hopefully help me better understand binding, XAML, and extension syntax.
So I simply want to change the binding source from the MainWindow to an object I have instantiated in MainWindow.
Here is my C# code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
favclass myfavclass = new favclass();
InitializeComponent();
this.DataContext = this;
}
string _myString = "hello";
public string MyString
{
get { return _myString; }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
public class favclass : INotifyPropertyChanged
{
int _myint = 34;
public int MyInt
{
get { return _myint; }
set { _myint = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
}
and my XAML
<Window x:Class="WpfApplication1.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" >
<Grid>
<TextBlock Height="50" Width="50" Text="{Binding MyString}"/>
<TextBlock Height="50" Width="48" Margin="200,100,100,100"
Text="{Binding Source=myfavclass, Path=MyInt}"/>
</Grid>
</Window>
So as you can see I want first display the MyString property from main window.
Then I want to display the MyInt from the myfavclass object. But of course MyInt doesn't appear. I've tried every variation I can think of.
What XAML am I missing? Why doesn't the XAML I have work?
Thanks
Source=myfavclass this is wrong. Source can be only assigned directly using element syntax like this:
<Binding>
<Binding.Source>
<!-- value here -->
</Binding.Source>
</Binding>
Or you can use StaticResource or DynamicResoure or some custom MarkupExtension like this:
Text="{Binding Source={StaticResource someKey}, Path=MyInt}"
Or use the new feature {x:Reference} to get reference directly to some named element inside XAML:
Text="{Binding Source={x:Reference someName}, Path=MyInt}"
Moreover the myfavclass is declared as local variable inside your code behind. There is no way it can be used (referenced) inside XAML code.
You're doing something called multiple viewmodels. If so you should provide multiple DataContext for your controls. I prefer to using nested viewmodels. To implement this, you can try modifying the MainWindow like this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
_myfavclass = new favclass();
InitializeComponent();
this.DataContext = this;
}
private readonly favclass _myfavclass;
//we will use this property inside XAML code
public favclass MyFavClass {
get {
return _myfavclass;
}
}
}
Now in XAML code, you can bind the Text to the MyFavClass.MyInt, note that the DataContext is implicitly the source for the Binding, so you just need to specify the Path:
<TextBlock Height="50" Width="48" Margin="200,100,100,100"
Text="{Binding Path=MyFavClass.MyInt}"/>
Your MyInt is not properly implemented using INotifyPropertyChanged (but I hope you already know that).
favclass myfavclass = new favclass(); should be declared out of the init method,or you won't get this.myfavclass instance

Binding UserControl to its own dependencyProperty doesn't work

Im having a problem where I can't create a User Control which uses properties of an custom object when the parent has set that object to data bind.
To try an explain what I mean here is the code.
Custom object:
public class MyObj
{
public string Text { get; set; }
public MyObj(string text)
{
Text = text;
}
}
User Control Code Behind:
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ObjectProperty =
DependencyProperty.Register("Object", typeof (MyObj), typeof (MyControl), new PropertyMetadata(default(MyObj)));
public MyObj Object
{
get { return (MyObj) GetValue(ObjectProperty); }
set { SetValue(ObjectProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
User control XAML:
<UserControl x:Class="Test.MyControl"
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="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding Object.Text}"/>
So all I expect is for MyControl to display a TextBlock with text showing whatever string is in MyObj.Text;
If I add the control in code, without any bindings, then this works Okay e.g.
MyControl myControl = new MyControl(){ Object = new MyObj("Hello World!") };
grid.Children.Add(myControl);
However if I try to use data binding this doesn't display anything, here is the code for MainWindow.
CodeBehind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyObj _Object;
public MyObj Object
{
get { return _Object; }
set
{
_Object = value;
OnPropertyChanged("Object");
}
}
public MainWindow()
{
InitializeComponent();
Object = new MyObj("HELLO");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
Could anyone point me in the right direction, I guess it's something to do with using relative source binding on the UserControl but I'm not sure.
Thanks
I've personally never used a relative self binding on a UserControl, so I'm unsure if it works. You may try setting the x:Name of your UserControl, and use that in the binding.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{Binding ElementName=window, Path=Object.Text}"/>
</UserControl>
Note that if a data-binding fails to bind at runtime, you should also see a related error message in the Output window.
it's been a long time .. but since there is a new technique i would like to post it here.
Compiled Time Binding : this is a new type of binding introduced with windows 10. this binding has a lot of performance benefits classic binding.
And the extra benefit you need not set any DataContext the Page or Control itself is the DataContext you can bind to anything in the page or Control
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text}"/>
</UserControl>
But does this work perfectly as you have imagined .. No!! not as u guessed.
and there is an answer to it .
Compiled time binding are by default set to OneTime as opposed to classic bindings that are se to OneWay.
so you need to explicitly set the mode to OneWay to ensure the value always updates.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text,Mode=OneWay}"/>
</UserControl>

Setting an object property with XAML

I have two custom UserControl in my project code: TableControl and DeckControl. In the code of the latter I would be able to access the former when it's needed. So in my DeckControl I implemented the following property:
private TableControl m_Table;
public TableControl Table
{
get { return m_Table; }
set { m_Table = value; }
}
The problem is that I'm not able to set the property from XAML code:
<Canvas Core:Name="Layout" Loaded="OnLayoutLoaded">
<Namespace:TableControl Core:Name="Table" Canvas.Left="0" Canvas.Top="0" Height="{Binding ElementName=Layout, Path=ActualHeight}" Width="{Binding ElementName=Layout, Path=ActualWidth}"/>
<Namespace:DeckControl Core:Name="Deck" Canvas.Left="50">
</Canvas>
I tried using Reference but compiler says that the method or opera
<Namespace:DeckControl Core:Name="Deck" Canvas.Left="50" Table="{Core:Reference Name=Table}">
I tried this but it isn't working either:
<Namespace:DeckControl Core:Name="Deck" Canvas.Left="50" Table="{Core:Static Table}">
I also tried using Binding:
<Namespace:DeckControl Core:Name="Deck" Canvas.Left="50" Table="{Binding ElementName=Table}">
Ok so... it's my first approach to XAML and I'm still working on it... but I really can't get it!
If you want to bind to a property in youe Model(window/Usercontrol) codebehind you have to set the DataContext in your Xaml.
There are may ways to do this but the simpliest is just naming your window or usercontrol and binding using ElementName.
Example for a Window:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="233" Width="143" Name="UI">
<Canvas DataContext="{Binding ElementName=UI}" > <!-- Set dataContext to Window -->
<Namespace:DeckControl Canvas.Left="50" Table="{Binding ElementName=Table}">
</Canvas>
</Window>
And if you want the Xaml to update when Table changes your code behind should implement INotifyPropertyChanged, this will inform the Xaml that the property has changed.
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private TableControl m_Table;
public TableControl Table
{
get { return m_Table; }
set { m_Table = value; NotifyPropertyChanged("Table"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
If your Table property is not a DependancyProperty you will have to chage this so you can bind.
Example:
public class DeckControl : UserControl
{
.......
// Using a DependencyProperty as the backing store for Table. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TableProperty =
DependencyProperty.Register("Table", typeof(TableControl), typeof(DeckControl), new UIPropertyMetadata(null));
public TableControl Table
{
get { return (TableControl)GetValue(TableProperty); }
set { SetValue(TableProperty, value); }
}
}
Also any property that is being binded outside the scope of the UserControl has to be a DependancyProperty.
Example:
public partial class DeckControl : UserControl
{
public DeckControl()
{
InitializeComponent();
}
private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
}
This will bind inside the usercontrol when it is a simple property as it is inscope.
<UserControl .....
d:DesignHeight="300" d:DesignWidth="300" Name="UI">
<TextBlock Text="{Binding MyProperty}" />
</UserControl>
This will not bind as its out of scope of the UserControl, MyProperty will have to be a DependancyProperty to bind here
<Window ....
Title="MainWindow" Height="233" Width="143" Name="UI">
<Grid>
<local:DeckControl MyProperty="{Binding Width}" /> // Will not bind
</Grid>
</Window>
Hope that makes sense :)

How to create a UserControl in the MVVM pattern?

Currently, within a real-world application development, I am struggling with the consumption of a custom UserControl in the MVVM pattern.
In my application, there is a DataGrid where the user can select an entry. The DataGrid's SelectedItem is TwoWay-bound to a field of the ViewModel set as DataContext. When the user selects an entry, the field is properly updated (tested). In the Page where holds the DataGrid, the field is bound through XAML to a DependencyProperty of a custom UserControl devised in the MVVM pattern : it bares its own ViewModel which is set as DataContext. The trouble is that the UserControl's DependencyProperty is not updated when the field changes even though the INotifyPropertyChanged interface is correctly implemented (see the comparison with a traditional control in the next minimal working example).
This example is constituted of a Label and bares ViewModelUserControl as a DataContext, UserControl1is consumed by the MainWindow and the binding is compared to that of a Label.
The file MainWindow.xaml:
<Window x:Class="UserControlWithinUserControlDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:UserControlWithinUserControlDataContext"
Title="MainWindow"
Height="350" Width="525"
>
<StackPanel Orientation="Horizontal"
>
<ListBox SelectedItem="{Binding Text, Mode=TwoWay}"
x:Name="listbox"
Height="150"
>
</ListBox>
<Local:UserControl1 Text="{Binding Text, Mode=OneWay}"
Height="50" Width="150"
/>
<Label Content="{Binding Text, Mode=OneWay}"
/>
</StackPanel>
</Window>
The code-behind MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ViewModelWindow view_model_window
{
get { return _view_model; }
}
private ViewModelWindow _view_model = new ViewModelWindow();
public MainWindow()
{
InitializeComponent();
DataContext = view_model_window;
IList<String> list = new List<String>();
list.Add("A");
list.Add("B");
list.Add("C");
listbox.ItemsSource = list;
}
}
The ViewModel of the MainWindow, the file ViewModelWindow.cs :
public class ViewModelWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "Bli";
}
The file UserControl1.xaml:
<UserControl x:Class="UserControlWithinUserControlDataContext.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:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="{Binding Text}"
Background="Magenta"
HorizontalAlignment="Stretch"
/>
</Grid>
</UserControl>
The code-behind file UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public ViewModelUserControl view_model_usercontrol
{
get { return _view_model; }
}
private ViewModelUserControl _view_model = new ViewModelUserControl();
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(TextPropertyChangedCallback)));
private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 user_control = d as UserControl1;
if(user_control != null)
{
user_control.view_model_usercontrol.Text = user_control.Text;
}
}
public UserControl1()
{
InitializeComponent();
DataContext = view_model_usercontrol;
}
}
The ViewModel of UserControl1, the file ViewModelUserControl.cs:
public class ViewModelUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "";
}
As you can see when executing this code, the MainWindow's Label gets updated while the UserControl1's Label doesn't.
What am I doing wrong? Is there a way to makes this works?
Many thanks in advance for any clue.
first you do not need to add anything in the UserControl just the XAML. Remove all the code of the UserControl and try.
Let's explain why:
Content="{Binding Text}" you set this in the usercontrol xaml, it's binded to the ViewModelWindow. and that works. and remove in
<Local:UserControl1 => Text="{Binding Text, Mode=OneWay}"
Ok, but it is correct to define a property in the user control in case of other situation?, that's right, in order to do that:
<UserControl x:Name="UserControlInstance"...>
<Label Content="{Binding Text, ElementName=UserControlInstance}" ...>
Where in this case Text is the dependency property and not the datacontext property.
Try the first option, and then the second defining just a Dependency Property and in this case bind the dependency property as you did.
And a tip, if a dependency property is in the visual element tree like in your case you do not need to call the callback.
Thank you Juan for your answer, here is the solution for conceiving the UserControl in the MVVM pattern:
I gave the name root to the Grid of UserControl1 and set its DataContext:
root.DataContext = view_model_usercontrol;
instead of:
DataContext = view_model_usercontrol;
Everything works fine.
Happy ending :)

Categories

Resources