programmatic WPF binding fails - wpf

I have a UserControl with a grid in it and I am generating a column of rectangles in my code behind. The UserControl has some dependency properties that I need to bind the rectangles to. I originally did this in the XAML with the following markup:
<Rectangle Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=MeterBarColor}"
Grid.Row="0"
Margin="2,2,2,0" />
This binding worked, however I need to build the column of rectangles dynamically so I tried to create a binding in my code behind like this:
Dim oBinding As New Binding("{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=MeterBarColor}")
oRectangle.SetBinding(Rectangle.FillProperty, oBinding)
When I run the application I get errors for each binding attempt saying the property was not found.
Hopefully someone can help me resolve this,
Sid

The argument of the Binding constructor you use is a path, not a {Binding ...} expression. Thus, you need to call New Binding("MeterBarColor") and then set the RelativeSource property.
Or you could use the parameterless constructor and object initializers:
Dim oBinding As New Binding() {
.RelativeSource = New RelativeSource(RelativeSourceMode.FindAncestor,
GetType(UserControl), 1),
.Path = "MeterBarColor"
}
oRectangle.SetBinding(Rectangle.FillProperty, oBinding)

Related

Setting attached property value as target of binding

I have been searching for an answer to this question, but always find the reversed of what I am looking for.
like this Question: WPF Attached Property Data Binding with following answer:
IsChecked="{Binding Path=(local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}"
But I would like to perform this kind of Binding in code-behind:
<Grid local:MyGridHelper.GridWidth = {Binding Elementname=XY, Path=ActualWidth} />
In code, setting a value for the attached Property is usually easy: MyGridHelper.SetGridWidth(mygrid, value). But how do I get a Binding into this instaed of just a value?

WPF: UserControl not displaying at design time inside Datatemplate for a ContentControl

I spent quite some days trying to figure out what is not working here, and I suspect it is eighter a bug in VS or something plain simple I oversee...
I have a ContentControl which serves a view model:
<!-- Learning control from DataTemplate -->
<ContentControl Content = "{Binding learningViewModel}"
Grid.Row = "1"
Grid.Column = "1"
Height = "300"
Margin = "20, 0, 0, 0"/>
The property it is bound to is actually an interface to a set of view models.
Then I have a definition of DataTemplate for the possible ViewModels:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:vmLearnSpeak}">
<local:viewLearnSpeak CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnWrite}">
<local:viewLearnWrite CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:vmLearnListen}">
<local:viewLearnListen CommandNotKnown = "{Binding DataContext.cmdNotKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandAlmostKnown = "{Binding DataContext.cmdAlmostKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}"
CommandKnown = "{Binding DataContext.cmdKnown, RelativeSource={RelativeSource AncestorType=local:wndSession}}" />
</DataTemplate>
</Window.Resources>
During programm execution the binding works as expected.
However during design time the ContentControl will only display a plain string representing the path of the view model (eg. "MyApp.ViewModels.vmLearnSpeak").
All view models have default constructors which initialize some dummy data and if I change that dummy initialization in the ViewModel that has the property the ContentControl is bound to, then the DataTemplate is also changed in designer as expected (the string changes to "MyApp.ViewModels.vmLearnWrite" for example).
If I replace the DataTemplate content by another control like a button, I get the button displayed if the correcponding Data for the DataTemplate with the button is set.
So in all, DataTemplate is basically working.
The custom controls work too: if I put the code from the DataTemplate directly in the layout, then the control appears as expected.
Hence it appears that the control will only fail to display during design time if placed inside a DataTemplate...
I hope I provided enough information, else let me know... Thanking you in advance for your support!
BTW: running Visual Studio 2016 communities, just in case there might be a known bug (After spending so much time trying to fix I am not sure but I believe that it used to work some time ago...)
EDIT:
The issue really is with the UserControl, if I add a simple empty or with only a button UserControl the same issue is there. Hoewver if i put a CustomControl or a regular Button, the DataTemplate is shown...
Just for information: apparently this was a bug in Visual studio that has been fixed, while I am not sure when it will release:
https://developercommunity.visualstudio.com/content/problem/1004742/usercontrol-is-not-displayed-inside-datatemplate-i.html

Bind the current instance of a control to an attached property

I'm looking to see if you can bind the current instance of a usercontrol or window to an attached property defined in its xaml, eg:
<Window MyAttachedProp.Value="{Binding Self}"/>
You want the MyAttachedProp.Value to have the Window object reference?
You can use any of these methods:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
give your Window an x:Name="XXXXX"...and then use {Binding ElementName=XXXXX} to find it.
{Binding RelativeSource={RelativeSource Self}}
{Binding RelativeSource={x:Static RelativeSource.Self}}
With example 4, it avoids the creation of a new RelativeSource object (with the Mode set to Self)...instead it points to the Static one already created in the RelativeSource class....(this is a VERY minor and premature optimization).
Most people use example 3 as it's less to type and clearer to read.
http://www.c-sharpcorner.com/uploadfile/yougerthen/relativesources-in-wpf/
{Binding RelativeSource={RelativeSource Self}}

How to pass CommandParameter by Binding through XAML on customcontrols

I've generated a CustomControl that includes (among other elements) a TextBox. Binding a value works:
(Code-Snippet from Generic.xaml)
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ParameterValue, Mode=TwoWay }"/>
Now, i wanted to add some ValueConverter to my Binding, so i implemented a ParameterConverter. Using the Converter works also (so far), i can see the value being converted.
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ParameterValue, Mode=TwoWay, Converter={StaticResource ParameterConverter}}"/>
Now as my converters logic got more complex, i wanted to use the parameter Property on my ParameterConverter. But unfortunately, as parameter is no DependencyProperty, i cannot bind anything to it. I've registered some DependencyProperty in my CustomControl, but i wasn't able to bind it to the ConverterParameter in my XAML. The desired ConverterParameter i want to bind to is a Enum called ParameterUnit.
What i expected the result should look like is something like this:
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ParameterValue, Mode=TwoWay, Converter={StaticResource ParameterConverter}, ConverterParameter='{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ParameterUnit}'}"/>
I've got a solution, but looks really nasty and violates the CCD-principles i'd like to follow always as far as possible. I added some code in my ParameterControl-Class:
public ParameterControl()
{
_textBox = (TextBox)Template.FindName("ParameterValueTextBox", this);
this.Loaded += (s, e) => SetupControl();
}
public void SetupControl()
{
var textBinding = new Binding();
textBinding.RelativeSource = RelativeSource.TemplatedParent;
textBinding.Path = new PropertyPath("ParameterValue");
textBinding.Converter = new ParameterToHumanFormatConverter();
textBinding.ConverterParameter = ParameterUnit;
textBinding.Mode = BindingMode.TwoWay;
textBinding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
_textBox.SetBinding(TextBox.TextProperty, textBinding);
}
Isn't there no better, cleaner and easier solution? I just can't believe there is no way to bind a ConverterParameter.
If you need more than one value binding just use a MultiBinding.

How do I databind to the control's property rather than the datacontext?

I have a sub control embedded inside my main control, it allows the user to edit an address. Because this is reused all over the place (sometimes in multiple places on one control) I bind it like so
<Controls:EditAddressUserControl DataContext="{Binding Path=HomeAddress}"/>
<Controls:EditAddressUserControl DataContext="{Binding Path=WorkAddress}"/>
But the EditAddressUserControl needs access to the main control's list of CountrySummary objects so it can choose which country the address belongs to.
I have added a Countries DependencyProperty to EditAddressUserControl and added
Countries="{Binding Countries}"
So far all is going well, the EditAddressUserControl.Countries property has the correct countries in it. However, how do I databind my Combobox.ItemsSource to that in XAML?
I still want everything on my EditAddressUserControl to bind to its DataContext, but the ComboBoxCountries.ItemsSource needs to bind to "this.Countries".
How do I do that?
I've tried this
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
I saw no binding errors in the output box, but I also saw no items in the combobox.
You can accomplish this by using a RelativeSource for the binding source, instead of the DataContext.
This would most likely look something like:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Controls:EditAddressUserControl}}, Path=Countries}" />
The way to do it was to stop using DataContext completely. Instead I added a DependencyProperty to my control
public static DependencyProperty AddressProperty = DependencyProperty.Register("Address", typeof(EditPostalAddressDto), typeof(EditPostalAddressControl));
Then in the parent control instead of setting DataContext="..." I set Address="..." - The XAML for the control is then changed to include an ElementName on the binding
<UserControl ..... x:Name="MainControl">
<TextBox Text="{Binding ElementName=MainControl,Path=Address.Region}"/>
Now I can specifically bind to the Address property, but also bind to properties on the main data context.

Resources