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'm trying to make a simple user control in WPF/C#. Everything seems to work except for databinding to property in datacontext.
I tried making extremely simplified example to see better what I'm doing wrong. I'm quite new to WPF in general, so I think I'm doing Dependency Properties wrong somehow. Might be in the binding itself, but that works normally for other elements, like TextBox.
This seemed to be similiar problem, but answer didn't seem to work in my case: Why DataBinding is not propagating to UserControl
MainWindow.xaml (root tag omitted)
<StackPanel>
<local:UserControl1 TextToDisplay="{Binding BoundableText}" />
<TextBlock Text="{Binding BoundableText}" />
</StackPanel>
UserControl1.xaml (root tag omitted)
<TextBlock Text="{Binding TextToDisplay}" />
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
namespace UserControlTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
BoundableText = "This text is bound";
}
public string BoundableText { get; set; }
}
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty TextToDisplayProperty =
DependencyProperty.Register(nameof(TextToDisplay), typeof(string), typeof(UserControl1), new PropertyMetadata("Default text"));
public string TextToDisplay
{
get => (string)GetValue(TextToDisplayProperty);
set => SetValue(TextToDisplayProperty, value);
}
public UserControl1()
{
InitializeComponent();
DataContext = this;
}
}
}
The 2 elements should be also identical in content, displaying text which is set in code-behind ("This text is bound"). TextBox works as it should, but UserControl1 has just the default text. What makes this case different from the first? Can I get it to work the same?
Note: I also tried to bind to other element's property, that works nicely. Also binding from code-behind works. However, this should be possible from xaml itself. In actual use case the control will be in a DataTemplate.
Don't set the DataContext of the UserControl to itself in the constructor:
DataContext = this;
Then it won't inherit the DataContext from the parent window.
To bind to the TextToDisplay dependency property in the UserControl, you could use a RelativeSource:
<TextBlock Text="{Binding TextToDisplay, RelativeSource={RelativeSource AncestorType=UserControl}}" />
Related
How to create a general user control using MVVM Light?
All the main views in the application seem to work fine. However, general controls doesn't seem to accept bindings. This is my FileDiplay control. An icon and a TextBlock displaying a filename next to it.
Utilization
In one of the main views, I try to bind a FileName inside an ItemsTemplate of an ItemsControl. Specifying a literal, like FileName="xxx" works fine, but binding doesn't.
<local:FileLink FileName="{Binding FileName}" />
I've been playing around with DependencyProperty and INotifyPropertyChanged a lot. And seemingly there's no way around a DependencyProperty, since it can't be bound otherwise. When using a simple TextBlock instead of this user control, binding is accepted.
I didn't include the locator or the utilizing control in order to avoid too much code. In fact, I think this is a very simple problem that I haven't found the solution for, yet. I do think that having the DataContext set to the ViewModel is correct, since no list binding or real UserControl separation is possible. I've also debugged into the setters and tried the different approaches.
FileLink.xaml
<local:UserControlBase
x:Class="....FileLink"
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:..."
mc:Ignorable="d" DataContext="{Binding FileLink, Source={StaticResource Locator}}">
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}" Margin="0,0,5,0" />
<TextBlock Text="{Binding FileName}" />
</StackPanel>
</Grid>
</local:UserControlBase>
FileLink.xaml.cs
using System.Windows;
using System.Windows.Media;
namespace ...
{
public partial class FileLink : UserControlBase
{
private FileLinkViewModel ViewModel => DataContext as FileLinkViewModel;
public static DependencyProperty FileNameProperty = DependencyProperty.Register(nameof(FileName), typeof(string), typeof(FileLink));
public ImageSource Icon
{
get
{
return App.GetResource("IconFileTypeCsv.png"); // TODO:...
}
}
public string FileName
{
get
{
return ViewModel.FileName;
}
set
{
ViewModel.FileName = value;
}
}
public FileLink()
{
InitializeComponent();
}
}
}
FileLinkViewModel.cs
using GalaSoft.MvvmLight;
namespace ...
{
public class FileLinkViewModel : ViewModelBase
{
private string _FileName;
public string FileName
{
get
{
return _FileName;
}
set
{
Set(() => FileName, ref _FileName, value);
}
}
}
}
Do not explicitly set the DataContext of your UserControl, because it effectively prevents that the control inherits the DataContext from its parent control, which is what you expect in a Binding like
<local:FileLink FileName="{Binding FileName}" />
Also, do not wrap the view model properties like you did with the FileName property. If the view model has a FileName property, the above binding works out of the box, without any wrapping of the view model.
If you really need a FileName property in the UserControl, it should be a regular dependency property
public partial class FileLink : UserControlBase
{
public FileLink()
{
InitializeComponent();
}
public static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register(nameof(FileName), typeof(string), typeof(FileLink));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
}
and you should bind to it by specifying the UserControl as RelativeSource:
<local:UserControlBase ...> <!-- no DataContext assignment -->
<StackPanel Orientation="Horizontal">
<Image Source="IconFileTypeCsv.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding FileName,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
</local:UserControlBase>
I'm trying to create a WPF UserControl which contains 2 buttons. I use this UserControl in a Window and apply a Window.Resource value to set the background of one button inside the user control.
Currently I have:
window.xaml
<Window.Resources>
<SolidColorBrush Color="Brown" x:Key="theBG"></SolidColorBrush>
</Window.Resources>
<theControl:TheControl
x:Name="TheControl"
buttonBG="{Binding Source={StaticResource theBG}}" />
usercontrol.xaml.cs
public SolidColorBrush buttonBG
{
get { return base.GetValue(buttonBGProperty) as SolidColorBrush; }
set { base.SetValue(buttonBGProperty, value); }
}
public static readonly DependencyProperty buttonBGProperty = DependencyProperty.Register("buttonBG", typeof(SolidColorBrush), typeof(DataPanel), null);
usercontrol.xaml
<Button ... Background="{Binding buttonBG}">
I was expecting this to work but the background is not the one I set in the window resource.
What am I doing wrong?
Background="{Binding buttonBG}"
Implies either that you changed the DataContext of the UserControl, which you should never do. Or that the binding is just wrong.
Use
Background="{Binding buttonBG, ElementName=control}"
Naming your UserControl root element control. RelativeSource works as well.
Try placing it in a separate model or even a viewmodel that has INotifyPropertyChanged. When you add view code in the cs for an xaml file, you need to bind with relativesource self and its hacky and goes against MVVM. I would create a seperate ViewModel with a Brush that has NotifyPropertyChanged baked into it. This will tell the UI to change everything its bound to on value change.
In the Window, bind your viewmodel to datacontext. In the viewmodel you can put:
private Brush _bgColor;
public Brush BgColor
{
get{return _bgColor;
}
set
{
_bgColor = value;
OnPropertyChanged("BgColor");
}
Create an ICommand, and bind your button to it like this in the viewmodel:
ICommand ChangeBgColor {get;set;
And in the XAML for the Button:
Command="{Binding Path=DataContext.ChangeBgColor,RelativeSource={RelativeSorce Mode=FindAncestor,AncestorType={x:Type UserControl}}"
This will fire the ICommand, bound to the viewmodel that is the datacontex of the window that you are working with.
And in that code for the ICommand change out your colors, you could do it like this:
private void OnChangeBgColor(object param){
var bc = new BrushConverter();
BgColor = (Brush)bc.ConvertFrom("#fff");
}
With the MVVM pattern, you want to get away from putting unnecessary code in the xaml.cs files and start putting them into viewmodels and models.
I have three projects in my solution:
My main WPF Application which contains a MainWindow + MainViewModel
UserControl Library with a UserControl (ConfigEditorView)
UIProcess class with the ViewModel for the UserControl (ConfigEditorViewModel)
In my MainWindow I want to use the UserControl with the ViewModel of UIProcess.
First I set the UserControl in my MainWindow:
<TabItem Header="Editor">
<Grid>
<cel:ConfigEditorView DataContext="{Binding ConfEditModel, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</TabItem>
I don't know which of these properties I need here, so I put all together but it still doesn't work.
Then I've set this in my MainViewModel:
public ConfigEditorViewModel ConfEditModel { get; set; }
With simple method that is bound to a Button:
private void doSomething()
{
ConfEditModel = new ConfigEditorViewModel("Hello World");
}
My ConfigEditorViewModel looks basically like this:
public class ConfigEditorViewModel : ViewModelBase
{
private string _Description;
public string Description
{
get
{
return _Description;
}
set
{
_Description = value;
base.RaisePropertyChanged();
}
}
public ConfigEditorViewModel(string t)
{
Description = t;
}
}
The description is bound to a TextBox in my UserControl.
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" Text="{Binding Description}"/>
When I start the application and click the Button the TextBox should contain "Hello World" but it's empty.
What I've done wrong?
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); }
}
Your view models (and, optionally, models) need to implement INotifyPropertyChanged.
Binding's aren't magic. There is no inbuilt mechanism that allows for code to be notified when a plain old property's value changes. You'd have to poll it in order to check to see if a change happened, which would be very bad, performance-wise.
So bindings will look at the objects they are bound against and see if they implement INotifyPropertyChanged and, if so, will subscribe to the PropertyChanged event. That way, when you change a property and fire the event, the binding is notified and updates the UI.
Be warned, you must implement the interface and use it correctly. This example says it's for 2010, but it works fine.
I've just scratched wpf and I have hopefully simple question.
How would you manage situtions like this
Inside ui wpf project there is MainWindow and two user controls. On default action I want to display usercontrol1 and usercontrol2 to be hidden. Both of this controls should have access to the repository instance for managing further work. Should I work with this repository instance with adding dependency property in each control or should I create viewmodel and work with viewmodel.
about binding
Let's say that I want to display inside MainControl listview some list of data. Please correct me
MainWindow.xaml
<my:MainControl x:Name="mainControl" Repository="{Binding}" />
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
ICarRepository repository = new CarRepository();
DataContext = repository;
}
MainControl.xaml.cs
public static readonly DependencyProperty RepositoryProperty = DependencyProperty.Register(
"Repository",
typeof(ICarRepository),
typeof(MainControl),
new PropertyMetadata(null));
public ICarRepository Repository
{
get { return (ICarRepository)GetValue(RepositoryProperty); }
set { SetValue(RepositoryProperty, value); }
}
public MainControl()
{
InitializeComponent();
DataContext = Repository.CountData();
}
MainControl.xaml
<Label Name="lblCountBooks" Content="{Binding}"/>
as I understand this should be fine, but I have one major obstacle here. I dont know how to pass repository instance from MainWindow to the MainControl. What I'm doing wrong here?
As noted in your comment, the Repository is null in your MainControl() constructor until after the constructor finishes and binding has occurred.
Instead, rely on the binding in the MainWindow. Also use a property on the ICarRepository instead of the CountData() method.
<Label Name="lblCountBooks" Content="{Binding Path=DataCount}"/>
assuming
public interface ICarRepository : INotifyPropertyChanged
{
int DataCount { get; }
}
If your MainControl must be explicit about accepting the Repository, you should respond to the DependencyProperty being set and update the DataContext.
public static readonly DependencyProperty RepositoryProperty =
DependencyProperty.Register("Repository",
typeof(ICarRepository),
typeof(MainControl),
new PropertyMetadata(OnRepositoryChanged));
private static void OnRepositoryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ICarRepository repository = e.NewValue as ICarRepository;
((MainControl)d).DataContext = repository;
}
When the binding from the MainWindow sets the Repository property of your MainControl, OnRepositoryChanged is called back to notify you. This is the correct tim eto update your DataContext.
The problem is that you are setting the DataContext of the MainControl in the Constructor of the MainControl. This, in effect, breaks the external binding.
Binding from the control owner to the control occurs after the construction of the control. For the binding to work, both the owner of the control and the control need to have the same DataContext.
Remove the binding from the Constructor of the Main Control and add it to the root content node. This means that the node will have the DataContext of the owner, allowing binding from the control owner to work, while the content will have a DataContext that is the UserControl.
<UserControl>
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:UserControl2}}, Path=Repository}">
<!-- content -->
</Grid>
</UserControl>
I have removed all the namespacing for brevity.
I hope this helps
I have wfp form like that:
public partial class MediaPlayerControlMain : Window
{
MediaPlayerMain MediaPlayerMain;
public MediaPlayerControlMain()
{
MediaPlayerMain = new MediaPlayerMain();
InitializeComponent();
}
}
I have my user control (PlayList) that use MediaPlayerMain object.
That User Control have that:
public partial class PlayList : UserControl
{
public MediaPlayerMain MediaPlayer
{
get { return (MediaPlayerMain)GetValue(MediaPlayerProperty); }
set { SetValue(MediaPlayerProperty, value); }
}
public static readonly DependencyProperty MediaPlayerProperty =
DependencyProperty.Register(
"MediaPlayer", typeof(MediaPlayerMain), typeof(PlayList),
new FrameworkPropertyMetadata()
);
}
Is there the way to set MediaPlayer property using just xaml. I tried to use "{Binding ElementName=MediaPlayerMain}" but it seems to be that MediaPlayerMain haven't initialized yet. Although i initialized it before InitializeComponent() function. What am i doing wrong?. And what is the best option to pass this object to my user control?
public partial class MediaPlayerControlMain : Window,INotifyPropertyChanged
{
public MediaPlayerControlMain()
{
InitializeComponent();
MediaPlayerMain = new MediaPlayerMain();
}
private MediaPlayerMain mediaPlayerMain;
public MediaPlayerMain MediaPlayerMain
{
get { return mediaPlayerMain; }
set { mediaPlayerMain = value; Notify("MediaPlayerMain"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
"{Binding MediaPlayerMain RelativeSource={RelativeSource AncestorType=Window}}"
The issue is you are trying to bind the field not property.For binding source must be the property not field because binding system uses reflection and looks only for properties not for fields.I hope this will help.
You need to name your root element (the Window/UserControl itself) in the markup. Ie:
<Window x:Name="mediaPlayer"
....>
<TextBlock Text="{Binding SomeProperty,ElementName=mediaPlayer}"
I don't see you setting the DataContext anywhere, and I don't think you have an object named MediaPlayerMain in your XAML tree
When you write {Binding ElementName=MediaPlayerMain}, you are telling WPF to set the property equal to the XAML object in the Visual Tree that is named MediaPlayerMain
What you're probably looking for instead is to bind to a Property in the DataContext named MediaPlayerMain, in which case your binding would look like this:
<local:PlayList MediaPlayer="{Binding MediaPlayerMain}" />
But that won't work unless your DataContext is set to an object containing the property MediaPlayerMain, such as setting this.DataContext = this in your Window's constructor.
As an alternative, you can use ElementName in your binding to tell WPF to look up the property on an object in the Visual Tree instead of in the DataContext, such as your Window.
<local:MediaPlayerControlMain x:Name="MainWindow" ...>
<local:PlayList MediaPlayer="{Binding MediaPlayerMain, ElementName=MainWindow}" />
</local:MediaPlayerControlMain>
This will make your binding look for the property MediaPlayerMain in the XAML element named MainWindow, which is your MediaPlayerControlMain class.
If you're new to WPF's DataBinding, I actually have an article on my blog specifically for beginners that should help you understand better what it is and how it works: What is this "DataContext" you speak of?