Dynamic Binding for Settings? - wpf

I am facing a problem, I have a application with several setting's files but which have exactly the same fields.
Sample :
Profil1.settings
Profil2.settings
Profil3.settings
What I'd like to do in my setting window XAML is to dynamicaly change the binding source for each profile.
At the moment my XAML binding look like this :
SelectedValue="{Binding Source={x:Static Local:Properties.Profil1.Default}, Path=CurrentProfil, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
My problem is how to replace the "Profil1" by "Profil2" without remaking all my Binding one by one for each controls via my code behind ? Is that possible to use some king of reflection variable in the XAML binding source and than simply change the type of that variable with Profil1 type or Profil2 type ?
Anyone can help me about this ?
Thank in advance.

What you want to do is set the DataContext of your settings window to the appropriate profile. You can do it anyway you want, but by doing so, all your bindings would point to that object. To make the bindings work like you have now, you'd do:
<Window x:Class="MyNamespace.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:MyNamespace"
DataContext="{Binding Source={x:Static Local:Properties.Profil1.Default}}">
...
<ComboBox SelectedValue="{Binding Path=CurrentProfil, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />

Related

DataGridColumnHeader of a DataGrid defined in a DataTemplate for a ViewModel

I have successfully used ProxyElement to pass Data Context of my Data Grid to DataGridColumnHeaders. However, I am trying out something new and I just can't figure out what I am doing wrong over here.
Here is what I am trying to do: I am creating a UserControl and associating it to my ViewModel in my Resources file (see Resources.xaml code snippet below).
Resources.xaml:
<ResourceDictionary
xmlns:myVm="clr-namespace:..."
xmlns:myUserControl="clr-namespace:...">
<DataTemplate DataType={x:Type myVm:DummyModel}">
<myUserControl:DummyUserControl />
</DataTemplate>
</ResourceDictionary>
Now in my UserControl, I have a DataGrid with a DataGridComboBoxColumn. I am trying to access my data context to set its item source and in the past I was able to do it using proxy element. This time however I am not able to do so. (See DummyUserControl.xaml code snippet below)
DummyUserControl.xaml:
<UserControl x:Class="Client.MyControl.DummyUserControl"
...>
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" x:Name="ProxyElement"
DataContext="{Binding}" />
</UserControl.Resources>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridComboBoxColumn
Header="Company"
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticResource ProxyElement}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataGrid>
</UserControl>
When I do this, my binding fails with the following message:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext.
BindingExpression:(no path); DataItem=null; target element is 'FrameworkElement'
(Name='ProxyElement'); target property is 'DataContext' (type 'Object')
I have no idea what to do here. I remember reading that the datacontext for a datatemplate is automatically set, so I have no idea why the data context is null in this case. To prove it is null, I also tried setting the binding in the code-behind file and added a breakpoint to check its value (which was null).
Can anyone suggest what to do here?
Edit 1
I have also tried the following approaches:
Remove ProxyElement altogether and see if it can detect DataContext. To no surprise, this failed.
Tried binding to the templated parent. Fail.
Tried binding to the UserControl itself. Fail.
I also tried referencing the data context of the parent item where this view model is going to be displayed, which is in a TabItem of a TabControl.
All of the alternate bindings gave me same error as the error above.
Here is a (working but not preferred) solution to this problem. You will realize why it is not preferred by the end of it.
The key to this problem is understanding what and how data context of a data template works. Whenever you define a Data Template for a View Model, the data context for the view that follows, whether it is a user control or just xaml itself, is the View Model! This shouldn't be a surprise to anyone.
But this will surprise people: if you specify a User Control, the Data Context of the User Control is not set during construction of the User Control! In other words, in the constructor of User Control, Data Context is going to be null. Furthermore, any XAML code that relies on the Data Context at construction time, which in this case was my FrameworkElement resource called ProxyElement got its DataContext set to null because it gets constructed at construction time of the User Control!
So when does the DataContext get set? Simply after the User Control is created. In pseudo code, this following describes the logic behind drawing a ViewModel:
Draw ViewModel x;
DataTemplate in ResourceDictionary says ViewModel x can be drawn using UserControl abc
Let's create a new instance of UserControl abc
Let's now assign the DataContext of abc to the ViewModel itself.
Let's return the newly created instance of UserControl abc
So what do we do to solve the problem in this question?
UserControl.xaml:
<UserControl ...
DataContextChanged="DaCoHasChanged">
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" /> <!--Remove DataContext="{Binding}"-->
</UserControl.Resources>
<DataGrid ...>
<DataGridComboBoxColumn
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticSource ProxyElement}}"
... />
</DataGrid>
</UserControl>
UserControl.xaml.cs:
private void DaCoHasChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var proxyElement = Resources["ProxyElement"] as FrameworkElement;
proxyElement.DataContext = e.NewValue; // instead of e.NewValue, you could
// also say this.DataContext
}
I am trying to figure out a way of getting rid of the code in the code-behind file. But till then, if someone else hits this problem, then they might be able to get inspired from this solution.
Credit to the concept behind this solution goes to: How to set the DataContext for a View created in DataTemplate from ViewModel
Try this
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridTemplateColumn Header="Company">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path="{Binding DataContext.ProductCompanies,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Now as I got your problem I think the problem is in DataGridComboBoxColumn I dont know why it not Binding using RelativeResource . Try it with DataGridTemplateColumn and you would not require any ProxyElement I hope this will help.

wpf debug error output System.WIndows.Data Error 25

I have a custom styled Combobox which works fine. It is placed inside a usercontrol and bound to a data structure. I use DisplayMemberPath to show only one element in the Combobox TextBox. The ComboBox Style is taken from MSDN and used many times. So it's not displayed here.
<UserControl x:Class="wpf.projext1.MyComboBox"
x:Name="MyControl"
...
<ComboBox Style="{StaticResource ComboBoxStyle}"
Text="{Binding ElementName=MyControl, Path=Text}"
IsEditable="True"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True"
ItemsSource="{Binding ElementName=MyControl, Path=MyItemsSource}"
DisplayMemberPath="Name"
</ComboBox
I get the following annoying error message populating the output window:
System.Windows.Data Error: 25 : Both 'ContentTemplate' and 'ContentTemplateSelector' are set; 'ContentTemplateSelector' will be ignored. ComboBoxItem:'ComboBoxItem' (Name='')
if i leave out the
DisplayMemberPath="Name"
... no debug output about error 25 is shown. But I definitely need DiplayMemberPath="Name"!
Do You have an idea to fix this ?
You canĀ“t set both DisplayMemberPath and ItemTemplate at the same time.
DisplayMemberPath is used to tell the ItemsControl which property to display when showing your objects. It makes no sense to set this field if you're already passing a custom ItemTemplate, since you can choose how to show the object within that ItemTemplate.
Since the default Combobox style from MSDN also sets an ItemTemplate, this is likely the cause of the error.
resolved: use the TextSearch attached property, no matter if TextSearch is enabled!
TextSearch.TextPath="Name"
Should note TextSearch.TextPath="Name" instead of DisplayMemberPath="Name".

Bind a ComboBox to two DataContexts

I have a ComboBox in my wpf application.
It's ItemsSource is binded to some table in my DataSet.
I need the text property to be binded to another's object property . I doesn't work because the ComboBox doesn't want to get two DataContexts. How can I solve this problem?
<StackPanel Width="Auto" Height="Auto" MinWidth="296" Orientation="Vertical" x:Name="MyStackPanel">
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding}" Text={Binding Path=MyProperty} />
</StackPanel>
In the code behind :
MyComboBox.DataContext = MyDataSet.Tables[MyTable];
MyStackPanel.DataContext = MyObject;
I want the ComboBox to show items from one DataContext but to show the text from another DataContext. How can I do it?
Don't use DataContext. Set the Source property of your bindings in XAML or create the bindings in code and set the Source property there.
Why are you assigning something to the datacontext of the stackpanel? From the looks of it, its not used.
Your code should work if MyDataSet.Tables[MyTable] returns an enumeration and contains a property called MyProperty.
What do you mean when you say that the combobox "doesn't want to get two DataContexts"?
Look into the properties IsEditable and IsReadOnly of the combobox.
Something like
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding}" Text={Binding ElementName=MyStackPanel Path=DataContext.MyProperty} />

Databinding issue with textbox and accessors

I am wondering what am I missing? The binding is not displaying at all in the textbox. These are my codes:
XAML Namespace:
xmlns:c="clr-namespace:mySystem.Workspace"
DataContext and Resources:
<Grid.Resources>
<c:Parameter x:Key="mySource"/>
</Grid.Resources>
<Canvas>
<Canvas.DataContext>
<Binding Source="{StaticResource mySource}" />
</Canvas.DataContext>
Textbox:
<TextBox x:Name="TextBox" Width="159" Height="26" Canvas.Left="36" Canvas.Top="47">
<TextBox.Text>
<Binding Path="JobKey" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
The class:
namespace mySystem.Workspace
{
public class Parameter : Object
{
The accessors:
public BasePar JobKey
{
get { return jobKey; }
set { jobKey = value; }
}
There are lots of odd things here but the most obvious one that will get you working is that the Binding Path is case sensitive.
Change your binding to:
<Binding Path="JobKey" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
This should get the binding working.
I'm also not sure what type BasePar is, or is meant to be, but unless you are doing something clever intentionally, just make it a standard type like string.
You should also probably not use the namespace System.Workspace, but something related to your own project.
After your response, the only thing I can guess that the BasePar object is intended for, is to be used within a DataTemplate, on an ItemsControl say. DataTemplates have the behaviour that when they do not know how to render an Object they will fall back the the Object's .ToString() method.
Now, in my comment I said that I don't think the TextBox can have a DataTemplate, and I believe this is true however I did find a trick at this Stackoverflow question which templates a content control and a textblock instead. The code is below:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:System.Workspace"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<c:Parameter x:Key="mySource"/>
<DataTemplate x:Key="MyDataTemplate">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</Grid.Resources>
<Canvas>
<Canvas.DataContext>
<Binding Source="{StaticResource mySource}" />
</Canvas.DataContext>
<ContentControl
Content="{Binding Path=JobKey}"
ContentTemplate="{StaticResource MyDataTemplate}" />
</Canvas>
</Grid>
I don't have time right now to get the TextBox working - don't even know if it is possible, given my first few tries. However, this might help get you where you need to go.
But still - if I was me I'd just use simple binding to standard objects. I can't see the benefit of the BasePar class in this scenario.
What does the BasePar implementation look like? Have a look in the Debug Output window to see if you have a line like this:
System.Windows.Data Error: 1 : Cannot create default converter to perform 'two-way' conversions between types 'WpfApplication1.BasePar' and 'System.String'. Consider using Converter property of Binding. BindingExpression:Path=JobKey; DataItem='Parameter' (HashCode=14209755); target element is 'TextBox' (Name='TextBox'); target property is 'Text' (type 'String')
This is telling you that you are trying to bind to the property, but WPF cannot create a 2-way binding, because it cannot convert the text (you type into the TextBox) into a 'BasePar' object.
As per David's suggestion, you could bind to a primitive string type, or alternately (as per the warning message above) you could add a Converter to the binding to convert a string into a BasePar.
you need to make jobkey a DependencyProperty by deriving it from DependencyObject or derive your class from INotifyPropertyChanged and add all the notify code, etc.
if you do not do this, then you will not receive update notifications and your bindings wont work as expected.
Path="jobKey"
You need to bind to the property not the field, i.e. make that upper-case. Also: To debug bindings check the Output-window in Visiual Studio.

WPF StaticResource bound to subproperty

I have a class thats created as a resource:
<Window.Resources>
<Model:MyModel x:Key="model" />
</Window.Resources>
The MyModel class has a cli property named Foo. I want the selected value in a combobox to be bound to this property. I thought I could do this but Im getting errors:
<ComboBox SelectedItem="{Binding Source={StaticResource model.Foo}}" />
Heres the error:
Cannot find resource named '{model.Foo}'.
Where did I go wrong? What extra parameters do I need to specify to properly bind to a subproperty?
You almost have it correct. You want to use a combination of the Binding's Path property and its Source property. So use one of the following (they are equivalent.)
{Binding Foo, Source={StaticResource model}}
or
{Binding Path=Foo, Source={StaticResource model}}
Hope this helps.

Resources