I'm new to WPF and I'm trying to figure out how data binding works, but I'm not having much luck.
I'm trying to start with something simple - binding the contents of a text box to a string variable in my program.
I read lots and lots of pages of MSDN documentation about data binding, XML namespaces, markup extensions, resources, dependency properties and whatnot, and I'm still not able to get it to work.
Here's my MainWindow.xaml:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfTest"
Title="MainWindow">
<Grid>
<Grid.Resources>
<c:Foo x:Key="MyFoo"/>
</Grid.Resources>
<TextBox Width="100" Height="28"
Text="{Binding Source=MyFoo,
Path=BarProperty,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
And my MainWindow.xaml.cs:
namespace WpfTest
{
public class Foo : DependencyObject
{
public static readonly DependencyProperty BarProperty = DependencyProperty.Register("Bar", typeof(String), typeof(Foo));
public String Bar
{
get { return (String)GetValue(BarProperty); }
set { SetValue(BarProperty, value); }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyFoo = new Foo { Bar = "hello" };
}
public Foo MyFoo { get; set; }
}
}
I would expect the text box to show "hello" when the program starts up, but it is empty.
Can someone tell me what I am doing wrong?
You need to set the DataContext of your Window to itself.
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MyFoo = new Foo { Bar = "hello" };
}
This tells WPF to look for bindings within your class.
Every control can set a DataContext which says "when I bind, I want to bind to a property on this specific instance... This is inherited, so if you set the DataContext of the MainWindow to itself, all controls inside of MainWindow will bind to properties on the MainWindow.
You need to specify the source. Either:
Give the window a name like Name="mywin", alter your binding witn ElementName="myWin"
Or set the window DataContext like:
DataContext="{Binding ElementName="myWin"} - you can also use a RelativeSource if you don't want the name I just couldn't post it untested - Bindings tend to require testing as you also noticed :)
This might help:
http://blogs.msdn.com/b/wpfsdk/archive/2006/10/19/wpf-basic-data-binding-faq.aspx
Related
I've made a user control which contains a command, to be called in response to a certain event. This command is a dependency property. I want to use it in the main window like this:
<local:myUserControl Command="{Binding someCommand}"/>
The "myCommand" is the dependency property I created for this user control. And I bind it to a command of the view model of the main window ("someCommand").
The problem is that I am setting the datacontext of my usercontrol (I have a view model for it), and it seems to reset the "Command" to null… Here is the code-behind of my view model:
public partial class myUserControl : UserControl, ICommandSource
{
public myUserControl()
{
this.DataContext = new myViewModel();
InitializeComponent();
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(myUserControl), new PropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(myUserControl), new PropertyMetadata(0));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(myUserControl), new PropertyMetadata(null));
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Command.Execute(this.CommandParameter);
}
}
The code of my user control could be the Following:
<UserControl x:Class="myApp.myUserControl"
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:myApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock MouseUp="TextBlock_MouseUp">
</TextBlock>
</Grid>
</UserControl>
(I know that this element seems a bit silly (or useless), but I have simplified it to test what didn't worked and also in order to ask a rather simple question).
I have discovered that, if I comment the "this.DataContext = new myViewModel();" line, the binding to the command works perfectly. And when I uncomment this line and put a breakpoint in the "TextBlock_MouseUp", the "Command" property is equal to null...
Would there be a way to resolve this problem? I have some complicated code in my view model (so I'm quite forced to keep this line "this.DataContext = new myViewModel();"), and I am not sure I could find another solution than having a "Command" dependency property in my user control…
To be sure I give a maximum of informations, I have the following code in the view model of my main window:
public ICommand someCommand { get; set; }
//Constructor
public MainWindowViewModel()
{
this.someCommand = new RelayCommand((obj) => { return true; },
(obj) =>
{
//I put a breakpoint here
int dummy = 0;
});
}
(The RelayCommand class is a standard RelayCommand class, with a "Predicate" CanExecute and an "Action Execute).
I hope this question is not a duplicate… I have found several similar question, but they did not seem to answer mine...
I'm really sorry for this question which was in fact a bit silly. I hadn't understand very well what happens during a binding. I thought that this code line in the MainWindow…
<local:myUserControl Command="{Binding someCommand}"/>
…would have made an attempt to bind the UserControl's "Command" property to the "someCommand" of the datacontext of the MainWindow. In fact, as #elgonzo pointed out, the binding looks up in the UserControl's datacontext for the "someCommand" property (and not in the MainWindow's datacontext!!). Therefore, setting the UserControl's datacontext with this line…
this.DataContext = new myViewModel();
...was preventing the binding to be correctly done (since it looks for the "someCommand" property of the UserControl's datacontext, which is now "myViewModel", which does not contain "someCommand"...).
To fix this, I had to change the binding like this:
<local:myUserControl Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.someCommand}"/>
I've found this solution here: https://stackoverflow.com/a/1127964/11609068.
Maybe it is not the best way to do it (the "Path= DataContext. someCommand" make me think this, it doesn't seem very elegant), but it works. Another way to do it is to name the MainWindow (x:Name="someName"), so that the binding is a bit simpler:
<local:myUserControl Command="{Binding ElementName=someName, Path=DataContext.someCommand}"/>
Again, sorry and many thanks to #elgonzo.
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.
Ok, we are trying out XAML for our GUI now (and learning as we go)...I have been able to do the data binding without a problem in XAML and C# independent of one another, but now comes the time I need to pass values back and forth and I'm a bit lost. When I compile and try to navigate to the page, it is throwing a XamlParseException: Specified class name doesn't match actual root instance type. Remove Class directive or provide an instance via XamlObjectWriterSettings.RootObjectInstance. Line 5 position 2.
Any help or a gentle shove in the right direction is greatly appreciated :)
Here's where I am:
namespace TheAirline.GraphicsModel.PageModel.PageFinancesModel
{
/// <summary>
/// Interaction logic for PageFinances.xaml
/// </summary>
public partial class PageFinances : Page
{
private Airline Airline;
public PageFinances(Airline airline)
{
InitializeComponent();
this.Language = XmlLanguage.GetLanguage(new CultureInfo(AppSettings.GetInstance().getLanguage().CultureInfo, true).IetfLanguageTag);
this.Airline = airline;
Page page = null;
//loading the XAML
using (FileStream fs = new FileStream("TheAirline\\GraphicsModel\\PageModel \\PageFinancesModel\\PageFinances.xaml", FileMode.Open, FileAccess.Read))
{
page = (Page)XamlReader.Load(fs);
}
//finding XAML element and trying to set the value to a variable
string airlineCash = GameObject.GetInstance().HumanAirline.Money.ToString();
TextBox cashValue = (TextBox)page.FindName("cashValue");
cashValue.DataContext = airlineCash;
}
}
}
And the first few lines of the XAML:
<Page
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:AirlineModel="clr-namespace:TheAirline.Model.AirlineModel"
mc:Ignorable="d"
x:Class="TheAirline.GraphicsModel.PageModel.PageFinancesModel.PageFinances"
xmlns:c="clr-namespace:TheAirline.GraphicsModel.Converters"
...>
</Page>
Bindings in XAML are resolved against the object that is assigned to the DataContext property of any given XAML element. The value of that property (as well as many other properties) Is Inherited in any given Visual Tree from parent elements to child elements.
for instance, given this class:
public namespace MyNamespace
{
public class ViewModel
{
public string Name {get;set;}
public bool IsActive {get;set;}
}
}
and this XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
FontSize="20">
<Window.DataContext>
<local:ViewModel>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Path=Name}"/>
<CheckBox IsChecked="{Binding Path=IsActive}"/>
<StackPanel>
</Window>
All four objects defined in XAML, the Window, the StackPanel, the TextBox, and the CheckBox, will have a FontSize of 20, and the instance of the ViewModel class assigned to their DataContext property. Therefore all bindings (Except bindings with a specified ElementName, RelativeSource, or Source) will be resolved against that instance.
It would be exactly the same if the property was assigned in code instead of in XAML:
public MyWindow() //Window Constructor
{
InitializeComponent();
this.DataContext = new ViewModel(); //Note that keyword "this" is redundant, I just explicity put it there for clarity.
}
Because of this, there is no need to set the DataContext property to each element explicitly, as the framework is already taking care of that.
Also, notice that in XAML, most built-in Markup Extensions have a default constructor convention that allows you to abbreviate their usage. In the case of the Binding Markup Extension, the default constructor has the Path property, therefore this:
<TextBox Text="{Binding Path=Name}"/>
is exactly the same as this:
<TextBox Text="{Binding Name}"/>
Now, for property changes in the underlying DataContext to be automatically passed from the binding source (ViewModel) to the binding target (XAML-defined objects), the source object must implement the System.ComponentModel.INotifyPropertyChanged interface and raise the PropertyChanged event every time a property changes.
Therefore, in order to support Two-Way Binding, the example class should look like this:
public namespace MyNamespace
{
public class ViewModel: INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
private bool _isActive;
public bool IsActive
{
get
{
return _isActive;
}
set
{
_isActive = value;
NotifyPropertyChanged("IsActive");
}
}
}
public void NotifyPropertyChanged (string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
Notice that the ViewModel class has no dependency or direct reference to any of the XAML-defined objects, but still it contains the Values of the properties that will appear in the UI. This allows for a complete decoupling between UI and application logic/data known as the MVVM Pattern. I strongly suggest you research on that topic if you expect to be successful in programming in C# + XAML, because it is a radical mindshift when compared to other, traditional UI paradigms.
For example, something like this is not recommended in XAML-based applications:
if (myWindow.CheckBox1.IsChecked)
//Do Something
because that would mean that you're coupling the application logic and making it dependant on the state of UI elements, which is precisely what you need to avoid.
Notice that all the links and all the concepts referenced in this answer pertain to WPF, but are also applicable to Silverlight and WinRT. Since you did not specify which of the three XAML-based frameworks you're using, I posted the WPF ones, which is what I'm most familiar with.
I am trying to use Data binding to bind an ObservableCollection to the ItemsSource of a DataGrid, as I learn about WPF and stuff.
In the code-behind I can set the DataContext with this.DataContext = this; or bloopDataGrid.DataContext = this;. That's fine and dandy.
I thought I could try something like
<Window.DataContext>
<local:MainWindow/>
</Window.DataContext>
in my main window, but this causes a Stack Overflow Exception as explained in this question. Fine, that makes some sense.
After reading this and other questions/answers that say to try DataContext="{Binding RelativeSource={RelativeSource Self}}" in the window's XAML code, I thought I could actually do this. Apparently I cannot. Or at least, the IDE lets me and it's syntactically correct, but does not do what I want (ie, exactly what this.DataContext = this; does).
Then I read this about using "{Binding ElementName=, Path=}" and tried to use it like so:
<DataGrid
Name="bloopDataGrid"
Grid.Row="1"
ItemsSource="{Binding ElementName=testWin, Path=OutputCollection}">
</DataGrid>
Which also doesn't work. Maybe not for the same reason, but I can't figure out the problem with it.
Oddly, I can't replicate the rebinding example shown in Rachel Lim's blog post.
XAML:
<Window
x:Class="DataBinding.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="testWin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding text}">
</Label>
<DataGrid
Name="bloopDataGrid"
Grid.Row="1"
ItemsSource="{Binding Path=OutputCollection}">
</DataGrid>
</Grid>
</Window>
C#:
using System;
using System.Collections.ObjectModel; //For ObservableCollection<T>
using System.Windows;
namespace DataBinding
{
public partial class MainWindow : Window
{
public String text { get; set; }
public ObservableCollection<testStruct> OutputCollection { get; set; }
public struct testStruct
{
public testStruct(String x, String y) : this()
{
Col1 = x;
Col2 = y;
}
public String Col1 { get; set; }
public String Col2 { get; set; }
}
public MainWindow()
{
InitializeComponent();
testA t1 = new testA();
this.DataContext = this;
//this.DataContext = t1;
//bloopDataGrid.DataContext = this;
text = "bound \"this\"";
t1.text = "bound a class";
OutputCollection = new ObservableCollection<testStruct>();
OutputCollection.Add(new testStruct("1", "2"));
OutputCollection.Add(new testStruct("3", "4"));
}
public class testA
{
public String text { get; set; }
}
}
}
The above code is what I'm using to test this, and is currently using the code-behind version which correctly gives me
What am I doing wrong, which is preventing me from getting the same results as the above picture but by using XAML for the DataContext handling? Am I not connecting the dots properly? ...am I missing some dots?
<Window.DataContext>
<local:MainWindow/>
</Window.DataContext>
is not the same as
this.DataContext = this;
The first one is creating a new instance of the MainWindow class and assigning that to the DataContext property of the Window, while the second is assigning the very same instance of the Window to its DataContext property.
In order to achieve that in XAML, you need to use a RelativeSource Binding:
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}">
</Window>
Edit:
The difference in behavior between defining the DataContext in XAML and in code behind is caused by the fact that the XAML is actually parsed when the constructor finishes executing, because the Dispatcher waits for the user code (in the constructor of the Window) to finish before executing its pending operations.
This causes the actual property values to be different in these different moments, and since there is no INotifyPropertyChanged, WPF has no way of updating the UI to reflect the new values.
You could implement INotifyPropertyChanged in the Window itself, but I suggest creating a ViewModel for this, as I don't like the fact of mixing INotifyPropertyChanged (which is more of a ViewModel concept) with DependencyObject-derived classes (UI elements).
I have trouble to understand how dependency properties can be used between C# and xaml code.
This is a smal code example of my question
XAML code:
<Window x:Class="WpfChangeTextApplication.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">
<StackPanel>
<Label Name="statusTextLabel" Content="{Binding StatusText}"></Label>
<Button Name="changeStatusTextButton" Click="changeStatusTextButton_Click">Change Text</Button>
</StackPanel>
C# code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string StatusText
{
get { return (string)GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StatusText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusTextProperty =
DependencyProperty.Register("StatusText", typeof(string), typeof(MainWindow));
private void changeStatusTextButton_Click(object sender, RoutedEventArgs e)
{
StatusText = "Button clicked";
}
}
So, my trouble is that Label statusTextLabel dose not get updated when I click on the button. My trouble is that I don't know in what part of the code that I'm doing something wrong, is it in the xaml or in the C#? In the xaml I might doing something wrong in the Binding? Or have I missed doing something in the C# code?
By default, binding paths are relative to the DataContext property of the current element. You have not set it to anything, so it can't resolve the binding. If you want the StatusText property on your window class, then there are two approaches. One is to use a binding with a RelativeSource of FindAncestor to find the Window in the tree and bind to its properties directly:
<Label Name="statusTextLabel" Content="{Binding StatusText,
RelativeSource={RelativeSource AncestorType=Window}}"></Label>
The other is to set the DataContext of the Window to itself, so it will be inherited by the label. For example, in your constructor:
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
For most applications, you will actually want a separate class to represent the data, and you will set an instance of that class as the DataContext. You can also use ordinary CLR properties instead of dependency properties, although you will need to implement INotifyPropertyChanged if you want to UI to be informed when properties change. Dependency properties are more useful when you are writing a custom control and you want users to be able to set the properties using data binding.