I have some object that is instantiated in code behind, for instance, the XAML is called window.xaml and within the window.xaml.cs
protected Dictionary<string, myClass> myDictionary;
How can I bind this object to, for example, a list view, using only XAML markups?
Update:
(This is exactly I have in my test code):
<Window x:Class="QuizBee.Host.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{Binding windowname}" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
</Grid>
</Window>
And in codebehind
public partial class Window1 : Window
{
public const string windowname = "ABCDEFG";
public Window1()
{
InitializeComponent();
}
}
Suppose the title should become "ABCDEFG" right? but it ends up showing nothing.
There's a much easier way of doing this. You can assign a Name to your Window or UserControl, and then binding by ElementName.
Window1.xaml
<Window x:Class="QuizBee.Host.Window1"
x:Name="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListView ItemsSource="{Binding ElementName=Window1, Path=myDictionary}" />
</Window>
Window1.xaml.cs
public partial class Window1:Window
{
// the property must be public, and it must have a getter & setter
public Dictionary<string, myClass> myDictionary { get; set; }
public Window1()
{
// define the dictionary items in the constructor
// do the defining BEFORE the InitializeComponent();
myDictionary = new Dictionary<string, myClass>()
{
{"item 1", new myClass(1)},
{"item 2", new myClass(2)},
{"item 3", new myClass(3)},
{"item 4", new myClass(4)},
{"item 5", new myClass(5)},
};
InitializeComponent();
}
}
You can set the DataContext for your control, form, etc. like so:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Clarification:
The data context being set to the value above should be done at whatever element "owns" the code behind -- so for a Window, you should set it in the Window declaration.
I have your example working with this code:
<Window x:Class="MyClass"
Title="{Binding windowname}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Height="470" Width="626">
The DataContext set at this level then is inherited by any element in the window (unless you explicitly change it for a child element), so after setting the DataContext for the Window you should be able to just do straight binding to CodeBehind properties from any control on the window.
While Guy's answer is correct (and probably fits 9 out of 10 cases), it's worth noting that if you are attempting to do this from a control that already has its DataContext set further up the stack, you'll resetting this when you set DataContext back to itself:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
This will of course then break your existing bindings.
If this is the case, you should set the RelativeSource on the control you are trying to bind, rather than its parent.
i.e. for binding to a UserControl's properties:
Binding Path=PropertyName,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
Given how difficult it can be currently to see what's going on with data binding, it's worth bearing this in mind even if you find that setting RelativeSource={RelativeSource Self} currently works :)
Just a little more clarification: A property without 'get','set' won't be able to be bound
I'm facing the case just like the asker's case. And I must have the following things in order for the bind to work properly:
//(1) Declare a property with 'get','set' in code behind
public partial class my_class:Window {
public String My_Property { get; set; }
...
//(2) Initialise the property in constructor of code behind
public partial class my_class:Window {
...
public my_class() {
My_Property = "my-string-value";
InitializeComponent();
}
//(3) Set data context in window xaml and specify a binding
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding My_Property}"/>
</Window>
In your code behind, set the window's DataContext to the dictionary. In your XAML, you can write:
<ListView ItemsSource="{Binding}" />
This will bind the ListView to the dictionary.
For more complex scenarios, this would be a subset of techniques behind the MVVM pattern.
One way would be to create an ObservableCollection (System.Collections.ObjectModel) and have your dictionary data in there. Then you should be able to bind the ObservableCollection to your ListBox.
In your XAML you should have something like this:
<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />
Define a converter:
public class RowIndexConverter : IValueConverter
{
public object Convert( object value, Type targetType,
object parameter, CultureInfo culture )
{
var row = (IDictionary<string, object>) value;
var key = (string) parameter;
return row.Keys.Contains( key ) ? row[ key ] : null;
}
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture )
{
throw new NotImplementedException( );
}
}
Bind to a custom definition of a Dictionary. There's lot of overrides that I've omitted, but the indexer is the important one, because it emits the property changed event when the value is changed. This is required for source to target binding.
public class BindableRow : INotifyPropertyChanged, IDictionary<string, object>
{
private Dictionary<string, object> _data = new Dictionary<string, object>( );
public object Dummy // Provides a dummy property for the column to bind to
{
get
{
return this;
}
set
{
var o = value;
}
}
public object this[ string index ]
{
get
{
return _data[ index ];
}
set
{
_data[ index ] = value;
InvokePropertyChanged( new PropertyChangedEventArgs( "Dummy" ) ); // Trigger update
}
}
}
In your .xaml file use this converter. First reference it:
<UserControl.Resources>
<ViewModelHelpers:RowIndexConverter x:Key="RowIndexConverter"/>
</UserControl.Resources>
Then, for instance, if your dictionary has an entry where the key is "Name", then to bind to it: use
<TextBlock Text="{Binding Dummy, Converter={StaticResource RowIndexConverter}, ConverterParameter=Name}">
Make your property "windowname" a DependencyProperty and keep the remaining same.
I was having this exact same problem but mine wasn't because I was setting a local variable... I was in a child window, and I needed to set a relative DataContext which I just added to the Window XAML.
<Window x:Class="Log4Net_Viewer.LogItemWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="LogItemWindow" Height="397" Width="572">
You can try x:Reference trick
<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>
That's my way to bind to code behind (see property DataTemplateSelector)
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataTemplateSelector = new MyDataTemplateSelector();
InitializeComponent();
// ... more initializations ...
}
public DataTemplateSelector DataTemplateSelector { get; }
// ... more code stuff ...
}
In XAML will referenced by RelativeSource via Ancestors up to containing Window, so I'm at my Window class and use the property via Path declaration:
<GridViewColumn Header="Value(s)"
CellTemplateSelector="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataTemplateSelector}"/>
Setting of property DataTemplateSelector before call InitializeComponent depends on missing implementation of IPropertyChanged or use of implementation with DependencyProperty so no communication run on change of property DataTemplateSelector.
Related
I'm loading data from a file and want to assign it to the View. I'm using a StaticResource binding type in the XAML, because it's easier to navigate within it when defining the View.
How do I reassign the value of the StaticResource defined in XAML to the data I just loaded?
XAML:
<Window.Resources>
<local:Container x:Key="Container"/>
</Window.Resources>
<Grid>
<ComboBox Name="Combo"
ItemsSource="{Binding Source={StaticResource Container}}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
Code-behind:
public class Container : ObservableCollection<string>
{
public Container()
{
Add("AAA");
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Container resource = FindResource("Container") as Container;
Container container2 = new Container() { "BBB" };
resource = container2;
}
}
Here, the Combobox is supposed to have "AAA" and "BBB", but the code-behind does not change it. I cannot set it to DynamicResource and using INotifyPropertyChanged interface doesn't help.
Of course doing Combo.ItemsSource = container2; works, but it then forces me to set all the bindings of the View elements in the code-behind. And how would I then set the RelativeSource bindings for all the child elements that need data from the top levels of the class hierarchy, i.e. now I can do {Binding Source={StaticResource Container}, Path=TopClass.AnotherCollection.Value} within a child element.
This is how you would replace the actual resource with another Container instance:
this.Resources["Container"] = container2;
But doing this won't affect the ComboBox unless you use a DynamicResource in your XAML markup:
<ComboBox Name="Combo"
ItemsSource="{DynamicResource Container}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
The value of a StaticResource markup extension is only resolved once.
I created UserControl with viewmodel. It has DependencyProperty which only works if the value is passed directly. If the value is passed through the binding, it no longer works.
Here is the view code:
This is a closed element not associated with any other. All listed items belong to him. This is a code shortening, I am not going to present whole, immeasurable structures.
View
public partial class SomeView : UserControl
{
public SomeView()
{
InitializeComponent();
SetBinding(ActiveProperty, new Binding(nameof(SomeViewModel.Active)) { Mode = BindingMode.OneWayToSource });
}
#region ActiveProperty
public static readonly DependencyProperty ActiveProperty = DependencyProperty.Register(nameof(Active), typeof(bool), typeof(VNCBoxView));
public bool Active
{
get { return (bool)GetValue(ActiveProperty); }
set { SetValue(ActiveProperty, value); }
}
}
VievModel
public class SomeViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool active;
public bool Active
{
get { return active; }
set
{
active = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Active)));
}
}
}
UserControl
<UserControl ...>
<UserControl.DataContext>
<viewModels:SomeViewModel />
</UserControl.DataContext>
<Grid>
<TextBlock Text="{Binding Active}" />
</Grid>
</UserControl>
===================================================
When working with a ready component, which is an individual, separate entity, the problem occurs depending on how it is used.
I remind you that the elements used in the view in question are a closed whole that does not connect with the element in which it is used. It is the transfer of value that is the matter of the problem.
This is working usage:
<local:SomeView Active="True" />
In viewmodel, the setter is invoked twice, once with false and then with true.
If the value comes from binding, it doesn't work:
<local:SomeView Active="{Binding SomeParentProperty}" />
In viewmodel, setter is only called once with the value false.
Setters in a view are never called, in both cases.
Please help
There is no IsConnected property in the SomeViewModel instance in the current DataContext of the UserControl, hence the Binding
<local:SomeView Active="{Binding IsConnected}" />
won't work. It tries to resolve the PropertyPath against the current DataContext, unless you explicitly specify its Source, RelativeSource or ElementName.
This is the exact reason why UserControls should never explicitly set their own DataContext, and hence never have something like an own, private view model.
The elements in the UserControl's XAML would not bind to properties of such a private view model object, but directly to the properties of the UserControl, for example like
<TextBlock Text="{Binding Active,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
When you set the DataContext explicitly in the UserControl like this:
<UserControl.DataContext>
<viewModels:SomeViewModel />
</UserControl.DataContext>
...you can no longer bind to SomeView's DataContext in the consuming view like this:
<local:SomeView Active="{Binding IsConnected}" />
...because SomeViewModel doesn't have any IsConnected property.
You should avoid setting the DataContext explicitly and let the UserControl inherit its DataContext from its parent element. You can still bind to the dependency property of the UserControl itself using a RelativeSource or an ElementName:
<UserControl ...>
<Grid>
<TextBlock Text="{Binding Active, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Grid>
</UserControl>
Besides, SomeViewModel seems superfluous in your example since the UserControl already has an Active property.
I'm a beginner on WPF and trying to bind the Items of a ComboBox to an ObservableCollection
I used this code:
XAML
<Window x:Class="comboBinding2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox x:Name="cmbTest" ItemsSource="{Binding Path=cmbContent}" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</Window>
C#
public MainWindow()
{
cmbTest.ItemsSource = cmbContent;
cmbContent.Add("test 1");
cmbContent.Add("test 2");
InitializeComponent();
}
public ObservableCollection<string> cmbContent { get; set; }
I don't get any errors on this Code until I try to debug, it throws the error:
TargetInvocationError
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in PresentationFramework.dll
Can anybody tell me what I'm doing wrong?
There are a few things wrong with your current implementation. As others have stated, your list is currently NULL, and the DataContext of the Window is not set.
Though, I would recommend (especially since you just started using WPF) is learning to do the binding the more 'correct' way, using MVVM.
See the simplified example below:
First, you want to set the DataContext of your Window. This will allow the XAML to 'see' the properties within your ViewModel.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Next, simply set up a ViewModel class that will contain all of the Window's binding elements, such as:
public class ViewModel
{
public ObservableCollection<string> CmbContent { get; private set; }
public ViewModel()
{
CmbContent = new ObservableCollection<string>
{
"test 1",
"test 2"
};
}
}
Lastly, update your XAML so that the binding path matches the collection:
<Grid>
<ComboBox Width="200"
VerticalAlignment="Center"
HorizontalAlignment="Center"
x:Name="cmbTest"
ItemsSource="{Binding CmbContent}" />
</Grid>
public MainWindow()
{
InitializeComponent();
cmbContent=new ObservableCollection<string>();
cmbContent.Add("test 1");
cmbContent.Add("test 2");
cmbTest.ItemsSource = cmbContent;
}
public ObservableCollection<string> cmbContent { get; set; }
The code above don't use any binding, that's mean using it there no need to bind the Combobox's ItemSource, if you wan't to use binding you need to
First: Set the DataContext from the CodeBehind (ViewModel) using :
this.DataContext=this;
or from the Xaml:
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Second : use the Binding in the ItemSource Just like you did ItemsSource="{Binding Path=cmbContent}" you may also considere using INotifyPropertyChanged Interface if you want to Notify the UI in case of any changes in a property
cmbContent is null because you never set it to anything. I'm guessing the error is actually a NullReferenceException, but it is showing up as TargetInvocationException because it is in the constructor of a view.
Also, you're setting the ItemsSource of the ComboBox twice (once in the binding, once in the constructor). You don't need to do that. Pick one. Your binding won't work the way it is written (because the DataContext isn't set) so you should either go with doing it in code, or set up the DataContext (as suggested by Nadia).
I like to create a UserControl with own Header Property.
public partial class SomeClass: UserControl, INotifyPropertyChanged
{
public SomeClass()
{
InitializeComponent();
}
private string header;
public string Header
{
get { return header; }
set
{
header = value;
OnPropertyChanged("Header");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
in UserContol xaml:
Label Name="lbHeader" Grid.Column="0" Content="{Binding Path=Header}"
If I set the value: AA2P.Header = "SomeHeeaderText"; than the label.Caption will not changed. How can I solve that problem?
In Windows xaml:
uc:SomeClass x:Name="AA2P"
If I give directly a value to label (lbHeader.Content = header;) instead of OnPropertyChanged("Header"); its work but, why it does not work with OnPropertyChanged?
I need to use DataContext for somethig else. I try to use dependency property but something is wrong.
public partial class tester : UserControl
{
public tester()
{
InitializeComponent();
}
public string Header
{
get { return (string)GetValue(MyDependencyProperty); }
set { SetValue(MyDependencyProperty, value); }
}
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(string));
}
<UserControl ... x:Name="mainControl">
<TextBlock Text="{Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
<Window ...>
<my:tester Header="SomeText" />
</Window>
It does not work. What I do wrong?
Thanks!
The easiest approach is to just the DataContext of your object. One way of doing that is directly in the constructor like this:
public SomeClass()
{
InitializeComponent();
DataContext = this;
}
Setting the DataContext will specify where new data should be fetched from. There are some great tips and information in the article called WPF Basic Data Binding FAQ. Read it to better understand what the DataContex can be used for. It is an essential component in WPF/C#.
Update due to update of the question.
To my understanding you should change the first argument of DependencyProperty.Register to the name of the property that you want to bind to, here "Header" as well as the second argument to the type of your class, here SomeClass. That would leave you with:
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("Header", typeof(SomeClass), typeof(string));
But i seldom use dependency properties so I am not positive that this is it, but its worth a try..
If you need the Data context for something else. You can also utilize the ElementName property in the Binding.
<UserControl
x:Class="MyControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="mainControl">
<TextBlock Text="Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
[Edit]
I should add something. Make the "Header" property a dependency property, this will make your live much easier. In UI Controls you should make property almost always a dependency property, every designer or user of your control will thank you.
The UserControl itself needs the DataContext of where it is used later. But the controls inside the UserControl need the UserControl as their DataContext, otherwise they also will inherit the DataContext from the later usage context. The trick is to set the DataContext of the UserControl's child to that of the UserControl, so it now can use the dependency properties of the UserControl.
<UserControl x:Class="MyControl.MyUserControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl,AncestorLevel=1}}">...</Grid>
</UserControl>
If you do this this way the children of the Grid can have simple {Binding dp's name} without additionally ElementName parameters.
How can I access the public variable which in Sample.xaml.cs file like asp.net <%=VariableName%>?
There are a few ways to do this.
Add your variable as a resource from codebehind:
myWindow.Resources.Add("myResourceKey", myVariable);
Then you can access it from XAML:
<TextBlock Text="{StaticResource myResourceKey}"/>
If you have to add it after the XAML gets parsed, you can use a DynamicResource above instead of StaticResource.
Make the variable a property of something in your XAML. Usually this works through the DataContext:
myWindow.DataContext = myVariable;
or
myWindow.MyProperty = myVariable;
After this, anything in your XAML can access it through a Binding:
<TextBlock Text="{Binding Path=PropertyOfMyVariable}"/>
or
<TextBlock Text="{Binding ElementName=myWindow, Path=MyProperty}"/>
For binding, if DataContext is not in use, you can simply add this to the constructor of the code behind:
this.DataContext = this;
Using this, every property in the code becomes accessible to binding:
<TextBlock Text="{Binding PropertyName}"/>
Another way is to just give a name to the root element of the XAML:
x:Name="root"
Since the XAML is compiled as a partial class of the code-behind, we can access every property by name:
<TextBlock Text="{Binding ElementName="root" Path=PropertyName}"/>
Note: access is only available to properties; not to fields. set; and get; or {Binding Mode = OneWay} are necessary. If OneWay binding is used, the underlying data should implement INotifyPropertyChanged.
For quick-and-dirty Windows in WPF, I prefer binding the DataContext of the Window to the window itself; this can all be done in XAML.
Window1.xaml
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBlock Text="{Binding Path=MyProperty1}" />
<TextBlock Text="{Binding Path=MyProperty2}" />
<Button Content="Set Property Values" Click="Button_Click" />
</StackPanel>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public static readonly DependencyProperty MyProperty2Property =
DependencyProperty.Register("MyProperty2", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.Register("MyProperty1", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public Window1()
{
InitializeComponent();
}
public string MyProperty1
{
get { return (string)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
public string MyProperty2
{
get { return (string)GetValue(MyProperty2Property); }
set { SetValue(MyProperty2Property, value); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Set MyProperty1 and 2
this.MyProperty1 = "Hello";
this.MyProperty2 = "World";
}
}
In the above example, note the binding used in the DataContext property on the Window, this says "Set your data context to yourself". The two text blocks are bound to MyProperty1 and MyProperty2, the event handler for the button will set these values, which will automatically propagate to the Text property of the two TextBlocks as the properties are Dependency Properties.
It is also worth noting that a 'Binding' can only be set on a DependencyProperty of a DependencyObject. If you want to set a non DependencyProperty (eg. a normal property) on an object in XAML, then you will have to use Robert's first method of using resources in the code behind.
myWindow.xaml
<Window
...
<TextBlock Text="{ Binding Path=testString }" />
</Window>
myWindow.xaml.cs
public partial class myWindow: Window
{
public string testString { get; set; } = "This is a test string";
public myWindow()
{
DataContext = this;
InitializeComponent();
}
}
Important
Set Datacontext
testString MUST be public
testString MUST be a property (have a get and set)