How to pass CommandParameter by Binding through XAML on customcontrols - wpf

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.

Related

programmatic WPF binding fails

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)

Data Binding works in code-behind, but not in XAML Possibly ItemsControl Issue

I have several bindings in my code, but I'm not sure why only this one fails in xaml.
<ItemsControl.ItemTemplate>
<DataTemplate>
<wpfExp:SignalGraph
x:Name="signal_graph"
Height="{Binding ElementName=signal_box, Path=GraphHeight, Mode=OneWay}"
<!--PenWidth="{Binding ElementName=signal_box, Path=GraphPenWidth, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"-->
Signal="{Binding}"
signal_graph_window_width="{Binding ElementName=signal_box, Path=signal_graph_window_width, Mode=OneWay}"
X_Scale="{Binding ElementName=signal_box, Path=X_Scale, Mode=OneWay}"
MaxTimeValue="{Binding Source = {StaticResource ResourceKey=signal_data}, Path = MaxTimeValue, Mode=OneWay}"
/>
only the commented line fails. Meanwhile, if I go into code behind:
private void SignalGraph_Loaded(object sender, RoutedEventArgs e)
{
loaded = true;
Binding b1 = new Binding();
b1.ElementName = "signal_box";
b1.Path = new PropertyPath("GraphPenWidth");
b1.Mode = BindingMode.OneWay;
b1.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(PenWidthProperty, b1);
Width = signal_graph_window_width;
SignalBox sb = VisualTreeUtilities.FindVisualParent<SignalBox>(this);
DrawSignals();
}
this works. To me they both seem the same.
But the only possible explanation that I can think of is that maybe when I write the binding in xaml, the Binding takes place at a different time rather than on load event like the code behind. If it is too early, before signalgraph is actually created, then maybe it doesn't bind correctly? but i'm not getting a binding error message. Quite confused.
So I figured out what was going on. Basically I accidentally had set a value to the DependencyProperty using the .net property setter and it was overwriting the binding. When I did the bind in code behind, I put the binding in the loaded event block, but in xaml, the binding was occurring in the constructor for the higher level control, so it would get overwritten later.
thanks for all the ideas and help thinking about it

WPF databinding with a user control

I have a wpf user control, which exposes a single custom dependency property. Inside the user control, a textblock binds to the value of the dp. This databinding works in all scenarios except when the data source is an object.
The minimal code necessary to reproduce this is:
this is the main part of the user control
<StackPanel Orientation="Horizontal">
<TextBlock Text="**SimpleUC** UCValue: "/>
<TextBlock Text="{Binding UCValue}"/>
</StackPanel>
and the user control code behind:
public SimpleUC()
{
InitializeComponent();
this.DataContext = this;
}
public string UCValue
{
get { return (string)GetValue(UCValueProperty); }
set { SetValue(UCValueProperty, value); }
}
public static readonly DependencyProperty UCValueProperty =
DependencyProperty.Register("UCValue", typeof(string), typeof(SimpleUC), new UIPropertyMetadata("value not set"));
this is the test window. I imported my project xml namespace as "custom"
<Window.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel>
<TextBlock Text="This fails to bind:"/>
<custom:SimpleUC UCValue="{Binding SomeData}"/>
</StackPanel>
<StackPanel>
<TextBlock>The same binding on a regular control like Label</TextBlock>
<Label Content="{Binding SomeData}"/>
</StackPanel>
<Slider x:Name="sld" />
<StackPanel>
<TextBlock>However, binding the UC to another element value, like a slider works</TextBlock>
<custom:SimpleUC UCValue="{Binding ElementName=sld,Path=Value}"/>
</StackPanel>
</StackPanel>
and the test window code behind is:
public TestWindow()
{
InitializeComponent();
this.DataContext = this;
}
//property to bind to
public string SomeData { get { return "Hello S.O."; } }
When I turn on the diagnostic tracing on the TestWindow, it spits out the error "BindingExpression path error:
'SomeData' property not found on 'object' ''SimpleUC' (Name='')' ... "
The binding expression is the same as the one I used in the neighboring label and it worked fine. This behavior seems really bizarre to me. Can anyone shed some light?
You set DataContext of your SimpleUC to itself here
public SimpleUC()
{
InitializeComponent();
this.DataContext = this; // wrong way!
}
so when you use binding here
<custom:SimpleUC UCValue="{Binding SomeData}"/>
it searches property SomeData in control's data context which is set to this object because code in SimpleUC constructor overrides value of DataContext and it is not set to TestWindow object anymore as you expected. That's why your solution works - it doesn't affect DataContext which is inherited from window. Also you can keep this.DataContext = this; but set element where to search property explicitly like this (skipped irrelevant)
<Window ... Name="wnd1">
<custom:SimpleUC UCValue="{Binding SomeData, ElementName=wnd1}"/>
...
But my oppinion is that your variant from the answer looks more convenient to me, setting data context to this is not very good practice.
Hope it helps.
If you must use a UserControl, your
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self},
Path=Parent.Parent.UCValue}"
/>
is an ok way to do it and
<TextBlock
Text="{Binding UCValue,
RelativeSource={RelativeSource FindAncestor,custom:SimpleUC,1}}"
/>
is better because you don't rely on the control hierarchy and possible instantiation order issues.
However I would recommend for this kind of situation that you use "custom controls" instead of "user controls". They take a little bit of getting used to, but they are much more powerful because their XAML is the template itself which means you can use TemplateBinding and {RelativeSource TemplatedParent}.
In any case, DataContext = this; is definitely to be avoided.

Binding property to Silverlight dependency property independent of DataContext

I'm trying to make an Address control that has an IsReadOnly property, which will make every TextBox inside read only when set to true.
<my:AddressControl Grid.Column="1" Margin="5" IsReadOnly="True"/>
I've managed to do this just fine with a dependency property and it works.
Here's a simple class with the dependency property declared :
public partial class AddressControl : UserControl
{
public AddressControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool),
typeof(AddressControl), null);
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
}
In the XAML for this codebehind file I have a Textbox for each address line:
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding City, Mode=TwoWay}"/>
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding State, Mode=TwoWay}"/>
<TextBox IsReadOnly="{Binding IsReadOnly}" Text="{Binding Zip, Mode=TwoWay}"/>
Like i said this works just fine.
The problem is that the Address control itself is bound to its parent object (I have several addresses I am binding).
<my:AddressControl DataContext="{Binding ShippingAddress, Mode=TwoWay}" IsReadOnly="True">
<my:AddressControl DataContext="{Binding BillingAddress, Mode=TwoWay}" IsReadOnly="True">
The problem is that as soon as I set DataContext to something other than 'this' then the binding for IsReadOnly breaks. Not surprising because its looking for IsReadOnly on the Address data entity and it doesn't exist or belong there.
I've tried just about every combination of binding attributes to get IsReadOnly to bind to the AddressControl obejct but can't get it working.
I've tried things like this, but I can't get IsReadOnly to bind independently to the AddressControl property instead of its DataContext.
<TextBox IsReadOnly="{Binding RelativeSource={RelativeSource Self}, Path=IsReadOnlyProperty}" Text="{Binding City, Mode=TwoWay}" />
I think I'm pretty close. What am I doing wrong?
With this answer (actually my own answer to a similar question) I have a good [better] solution.
I still have to iterate through the textboxes, but I don't have to set the actual value. I can create bindings in the codebehind - just not with XAML.
I think you're stuck, at least, if you want to do this just via binding. My guess is that you're going to have to resort to code-behind, presumably by iterating through your child textbox controls and setting their IsReadOnly propert as a side-effect of your Address control's IsReadOnly property.
Unlike some folks who think that any code sitting in a code-behind file is effectively an admission of failure, I don't get religious about it: if throwing some code into a code-behind is the easiest way to do something, that's where I put my code. On the contrary, if I have to spend half a day trying to figure out how to do something via binding that I could do in five minutes with a code-behind, that's failure, IMO.

WPF - Programmatic Binding on a BitmapEffect

I would like to be able to programmatically bind some data to the dependency properties on a BitmapEffect. With a FrameworkElement like TextBlock there is a SetBinding method where you can programmatically do these bindings like:
myTextBlock.SetBinding(TextBlock.TextProperty, new Binding("SomeProperty"));
And I know you can do it in straight XAML (as seen below)
<TextBlock Width="Auto" Text="Some Content" x:Name="MyTextBlock" TextWrapping="Wrap" >
<TextBlock.BitmapEffect>
<BitmapEffectGroup>
<OuterGlowBitmapEffect x:Name="MyGlow" GlowColor="White" GlowSize="{Binding Path=MyValue}" />
</BitmapEffectGroup>
</TextBlock.BitmapEffect>
</TextBlock>
But I can't figure out how to accomplish this with C# because BitmapEffect doesn't have a SetBinding method.
I've tried:
myTextBlock.SetBinding(OuterGlowBitmapEffect.GlowSize, new Binding("SomeProperty") { Source = someObject });
But it doesn't work.
You can use BindingOperation.SetBinding:
Binding newBinding = new Binding();
newBinding.ElementName = "SomeObject";
newBinding.Path = new PropertyPath(SomeObjectType.SomeProperty);
BindingOperations.SetBinding(MyGlow, OuterGlowBitmapEffect.GlowSizeProperty, newBinding);
I think that should do what you want.

Resources