How to bind a ConvertorParameter - c#

I am trying to bind the value of a ConverterParameter. Currently finding it too tricky...
Codebehind
public static readonly DependencyProperty RecognitionProperty = DependencyProperty.Register("RecognitionToEdit", typeof(Recognition), typeof(RecognitionInstancesWindow), null);
public Recognition Recognition
{
get { return (Recognition)GetValue(RecognitionProperty); }
set { SetValue(RecognitionProperty, value); }
}
XAML of a TextBox, which forms part of a datatemplate for a coverflow type control.
<TextBlock HorizontalAlignment="Left" Margin="2,0,0,0" Text="{Binding Converter={StaticResource DateConverter}, Path=Date, ConverterParameter={Binding Recognition, Path=Frequency}}" />
Can anyone see where I'm going wrong please?

Unfortunately it is not possible, that's because for property to be bindable it should be dependency, and the object should be derived from DependencyObject. Binding is not derived from DependencyObject, so it is impossible, you should look another ways to do that
One way to do that is to create a class in static resource, and pass that class to your converter like this
<namespace:MyClass x:Key="MyClass">
<Binding ... ConvertParameter={StaticResource MyClass}/>
from MyClass you can return anything you want ;)
this post can be helpful

Related

changing the bound object in a datatemplate

Data templates are great, but I'm having a problem with binding in a particular situation. I have a class, Value, that has various descendants like StringValue, DateValue, etc. These Values show up in a Listbox. This template works fine, binding to a specific property of StringValue:
<DataTemplate DataType="{x:Type values:StringValue}">
<TextBox Margin="0.5"
Text="{Binding Path=Native}" />
</DataTemplate>
However, when I bind to an object itself, instead of a specific property, the changes don't update the object, as in this template:
<DataTemplate DataType="{x:Type values:LookupValue}">
<qp:IncrementalLookupBox SelectedValue="{Binding Path=., Mode=TwoWay}"
LookupProvider="{Binding ElementName=EditWindow, Path=ViewModel.LookupProvider}">
</qp:IncrementalLookupBox>
</DataTemplate>
IncrementalLookupBox is a UserControl that ultimately allows a user to select a LookupValue, which should replace the item bound in the template. If this was bound to a simple type like an int or string, the binding would replace the object, so I'm not sure what the difference is with a more complex object. I know that the IncrementalLookBox is working, because binding some textboxes to the properties of SelectedValue (which is a dependency property) shows the correctly selected LookupValue.
In case it makes the situation more clear, here is the implementation of SelectedValue:
public LookupValue SelectedValue
{
get { return (LookupValue)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(LookupValue), typeof(IncrementalLookupBox), new PropertyMetadata(OnSelectedValuePropertyChanged));
private static void OnSelectedValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as IncrementalLookupBox;
obj.OnSelectedValuePropertyChanged(e);
}
private void OnSelectedValuePropertyChanged(DependencyPropertyChangedEventArgs e)
{
CheckForSelectedValueInLookups();
}
If all else fails consider using a ValueConverter to get the value you require.
Edit: this does not work. See link in comments below.
Make sure your class implements INotifyPropertyChanged and raise PropertyChanaged here:
private void OnSelectedValuePropertyChanged(DependencyPropertyChangedEventArgs e)
{
CheckForSelectedValueInLookups();
// RaisePropertyChanged();
}
My issue is the same as described here:
WPF TwoWay Binding of ListBox using DataTemplate
Apparently if I don't write enough text here, my answer will be converted to a comment and not close out the question. So, to summarize the issue, a two-way Binding=. in a datatemplate used in a ListBox (or any ItemsControl I image) won't work, because it is not the object itself being bound, but the ListBoxItem that contains it.

How do I use an ObjectDataProvider to set the value of text in a textbox from a method?

Background: I'm new to WPF and have been trying to teach myself for a couple weeks now. I have an app that runs in a NavigationWindow with a few pages. The page in question has 5 textboxes, 4 of which are backed with dependency properties. Three of them are set up with ValidationRules for times, the fourth has ValidationRules for type double. The fifth textbox is the output of the calculation made from a button click event. The button is bound to a MultiDataTrigger, which enables the button when there are no validation errors. Buddy says "hey you have everything bound already, why not update the output box on binding so you don't have to click a button?".
This seems like a good idea and a nice weapon to put in my wpf toolbox. The button serves two purposes, to calculate the time for the output textbox, and to offer to navigate to another page with the current values. If I could show the result of the calculation in the textbox with a binding, I would just use the button to navigate to the next page. I've tried setting up an ObjectDataProvider to use with the fifth textbox so I can call a method to populate the result with a binding. So far I've only succeeded in causing numerous errors, including causing a stackoverflow on the page call to InitializeComponent();
public static readonly DependencyProperty timeBoxProperty =
DependencyProperty.Register("timeBox", typeof(string),
typeof(TtlPage), new UIPropertyMetadata("07:30"));
public static readonly DependencyProperty timeBoxProperty2 =
DependencyProperty.Register("timeBox2", typeof(string),
typeof(TtlPage), new UIPropertyMetadata("13:00"));
public static readonly DependencyProperty timeBoxProperty3 =
DependencyProperty.Register("timeBox3", typeof(string),
typeof(TtlPage), new UIPropertyMetadata("13:40"));
public static readonly DependencyProperty hoursBoxProperty =
DependencyProperty.Register("hoursBox", typeof(string),
typeof(TtlPage), new UIPropertyMetadata("9.00"));
public string timeBox
{
get { return (string)GetValue(timeBoxProperty); }
set { SetValue(timeBoxProperty, value); }
}
public string timeBox2
{
get { return (string)GetValue(timeBoxProperty2); }
set { SetValue(timeBoxProperty2, value); }
}
public string timeBox3
{
get { return (string)GetValue(timeBoxProperty3); }
set { SetValue(timeBoxProperty3, value); }
}
public string hoursBox
{
get { return (string)GetValue(hoursBoxProperty); }
set { SetValue(hoursBoxProperty, value); }
}
Part of button click, given the above, should I be accessing the textbox.text like below using the Textbox.Name property, or should I be grabbing it from the property or DependencyProperty above?:
private void Button_Click(object sender, RoutedEventArgs e)
{
DateTime inTime = DateTime.Parse(ttlInTime.Text);
DateTime outLunch = DateTime.Parse(ttlOutLunch.Text);
DateTime inLunch = DateTime.Parse(ttlInLunch.Text);
decimal hours = decimal.Parse(ttlHours.Text);
//etc.
}
The method for the ObjectDataProvider:
public string UpdateOutput()
{
//do stuff
}
Some XAML ObjectDataProvider, one of the input textboxes, and the output textbox:
<ObjectDataProvider x:Key="outputBox" ObjectType="{x:Type sys:String}" MethodName="UpdateOutput"/>
<Style x:Key="timeBox3" TargetType="TextBox" BasedOn="{StaticResource tbStyle}">
<Setter Property="Text">
<Setter.Value>
<Binding ElementName="This" Path="timeBox3" UpdateSourceTrigger="
<Binding.ValidationRules>
<local:TimeValidation/>
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
</Style>
<TextBox Name="ttlInLunch" Style="{StaticResource timeBox3}" Grid.Row="2" Grid.Column="1" TextChanged="TimeBox_TextChanged"
GotFocus="TimeBox_GotFocus"/>
<TextBox Margin="0,2,2,1" Name="ttlOutput" Grid.Row="4" Grid.Column="1" IsReadOnly="True" Background="Transparent" IsTabStop="False"
Text="{Binding Source={StaticResource outputBox}}"/>
So, I've been here http://msdn.microsoft.com/en-us/library/aa348824(v=vs.110).aspx, and worked with the example, and after a while, realized that the ObjectType wasn't supposed to be the return type of the method. It was actually just the name of the containing class, so I used ttlPage as the type (which is the page itself ttlPage : Page), and caused a stack overflow. I've done a ton of Googling and haven't come up with anything helpful. I haven't created any sort of converter for it, because the method returns a string, which I would assume is suitable for the textbox.text property. I've set a breakpoint in the UpdateOutput method, and have found that it doesn't even get called. How do I call the UpdateOutput method and have it's result bound to the output textbox while the user is typing? As far as when I calculate, I was just going to return from the method until there are no validation errors, at which point I would perform my calculations and return the calculated value ToString();
Try changing the access modifier to public for your method UpdateOutput. Currently it's a private method, so can't be executed by the framework.
public string UpdateOutput()
{
//do stuff
}
Bartosz was correct, I needed to define another class to hold my UpdateOutput method. There were also several other factors which contributed to the frustration. First I created a class to hold the method. I then found out the hard way that I forgot a default constructor on said class. Additionally I found I was not able to use DependencyProperties as parameters for the ObjectDataProvider. I removed the entire set of DependencyProperties and their respective bindings. The styles referencing these were also removed, as were the bindings to the validation class.
//the containing class
public partial class AutoFillBox
{
public AutoFillBox()
{
//dont forget a default constructor
}
public string UpdateOutput(string time1, string time2, string time3, string time4)
{
//do stuff
}
}
The ObjectDataProvider:
<ObjectDataProvider ObjectType="{x:Type local:AutoFillBox}" MethodName="UpdateOutput" x:Key="odpOutput">
<ObjectDataProvider.MethodParameters>
<sys:String>08:00</sys:String>
<sys:String>12:00</sys:String>
<sys:String>13:00</sys:String>
<sys:String>18:00</sys:String>
</ObjectDataProvider.MethodParameters>
Then it was simply bind the appropriate textboxes to the MethodParameters:
<TextBox Name="recIn" Style="{StaticResource tbStyle}" Grid.Row="1" Grid.Column="1"
TextChanged="TimeBox_TextChanged" GotFocus="TimeBox_GotFocus">
<TextBox.Text>
<Binding Source="{StaticResource odpOutput}" Path="MethodParameters[0]" BindsDirectlyToSource="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:TimeValidation/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
And bind the output of the method to a textbox control:
<TextBox Margin="0,2,2,1" Name="recOutput" Grid.Row="5" Grid.Column="1" IsReadOnly="True" Background="Transparent" IsTabStop="False"
Text="{Binding Source={StaticResource odpOutput}, Mode=OneWay}"/>

Binding from DependencyProperty of a UserControl not working

I have a UserControl with one DependencyProperty which sets in codebehind (I guess this may be a source of my problem, but still don't know what to do):
UserControl
public partial class MyControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
public static string GetText(DependencyObject obj)
{
return (string)obj.GetValue(TextProperty);
}
public static void SetText(DependencyObject obj, string value)
{
obj.SetValue(TextProperty, value);
}
private void ChangeText()
{
Text="some value";
}
}
In my View.xaml I use this control like this:
<MyControl Text="{Binding Text, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
And the Text property in my ViewModel:
private string _text;
public string Text
{
get { return _text; }
set { _text= value; InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
The problem:
Text property in the ViewModel never gets updated; when use binding with a regular control like TextBox, all works perfect; if I set Text in XAML, Text propery of UserControl updates.
What I did wrong?
UPDATE
My issue was that I have set DataContext explicitly on MyControl.
Issue is in your Binding:
Text="{Binding Text, RelativeSource={RelativeSource Self},
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Text property is in your ViewModel but you are referring to itself by using RealtiveSource to point back to self. So, it's binding Text DP with itself.
If you have set DataContext of your control, it will automatically inherit DataContext from parent. So, you don't need RelativeSource at all.
It simply should be:
Text="{Binding Text}"
Few points more (but not related to your issue):
Since you target to use this property from within control, so go for normal DP instead of attached property.
Since at time of registration, you have set it to bind TwoWay by default. No need to explicitly do that at time of binding.
Remove InvokePropertyChanged call from your DP wrapper setter. Setter won't be called from XAML and also DP is already PropertyChanged aware.
UPDATE
In case DataContext of MyControl is set to instance of another class, above approach will search for Text property in MyControl DataContext.
You can pass DataContext of parent control (StackPanel in your case) like this:
Text="{Binding DataContext.Text, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=StackPanel}}"
You have registered your property as attached, yet you are also using it as a regular DependencyProperty. I think that the xaml parser gets confused. Decide which one you want to use.

WPF UserControl Binding Problem

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.

Issue with binding custom DependencyProperty to ListView.SelectedItem

I'm having an issue with a binding that I'm trying to implement. It will update the DP once, but after that, it's never updated again.
In XAML I have two controls binding to a listview.selected item.
<controls:MapControl DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" MapData="{Binding .}">
and
<TextBlock DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" Text="{Binding Name}" />
The textblock update as expected with each change of the listview's selected item.
My custom control creates the dependency property like so:
public class MapControl : UserControl
{
public MapData MapData
{
get { return (MapData)GetValue(MapDataProperty); }
set { SetValue(MapDataProperty, value); }
}
public static readonly DependencyProperty MapDataProperty =
DependencyProperty.Register("MapData", typeof(MapData), typeof(MapControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnMapDataPropertyChanged),
new CoerceValueCallback(OnMapCoerceValue)
)
);
private static void OnMapDataPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
((MapControl)source).MapData = (MapData)e.NewValue;
}
}
private static object OnMapCoerceValue(DependencyObject dpo, Object obj)
{
return obj;
}
...
}
I'm pretty much at my wits end and not sure what I should do from here. Any help is greatly appreciated.
Not sure exactly what you're trying to achieve or why your code appears so convoluted. If you explain more someone may be able to provide you with a much simpler solution.
That said, by the sounds of it the problem is simply that you're overwriting the binding with a local value. This looks like the culprit:
((MapControl)source).MapData = (MapData)e.NewValue;
When you do this, the MapControl.MapData property will no longer be bound to '.' Instead, it will take on whatever value you've assigned. So your MapControl.DataContext property is likely perfectly correct, but it's not being transferred to the MapData property because you've destroyed the binding.
I had the same error last week. My solution was simple : When you explicitly define a DependencyProperty you must also explicitly define the mode to TwoWay.
<TextBlock DataContext="{Binding ElementName=availableMapsListView,
Path=SelectedItem}"
Text="{Binding Name, Mode=TwoWay}" />

Categories

Resources