Can a WPF converter access the control to which it is bound? - wpf

Long version:
I have a simple WPF converter than does date conversions for me. The converter basically checks the date and formats it in dd/M/yyyy format. The converter does some smarts with the handling of the date which means that the user could type "23061971" and the value will be converted to "23/06/1971".
All this is trivial and working. The problem is that when I update the value, it does not update the caret position. Assume "|" is the caret and the user types "23061971|" then a millisecond later it is updated to "230619|71".
What I'd like to do is detect if the caret at the end of the value - if so, shift it to the end of the edit field once the new value has been updated. In order to do this, I'll need access to the edit control to which the converter is attached.
Short version:
From A WPF converter, can I get a reference to the control that is bound to that converter?

Here is an excellent article on how to get direct access to the control from within a converter: http://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx
Essentially:
<MultiBinding Converter="{StaticResource MyConverter}" >
<Binding RelativeSource="{RelativeSource Self}" Mode="OneTime"/>
<Binding Path="MyValue2" />
</MultiBinding>
in the converter values[0] will be your control, ready for casting and values[1] would be the data that you are binding.

In a ValueConverter you can't get access to the control - however you can get access if you use a multibinding with a multivalueconverter.
In the multibinding the first binding is your binding as now - without the converter. The second binding you bind to the control itself - there you go acces to the control.
I have used this approach to gain other things also - you can make "dummy" bindings to properties you want to trigger updates, i.e. if you bind to the control itself you will only get updated if it changes, not if a property does - so create dummybindings for those.

You can send the control with the MultiBinding like this.
<TextBox Height="100" x:Name="textbox1" DockPanel.Dock="Top">
<TextBox.Text>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding ElementName="textbox1" Path="." />
</MultiBinding>
</TextBox.Text>
</TextBox>

I don't thknk a converter has any way to get at the control that is using it. It is only a simple piece of logic that converts one object to another object.
However, in you case, perhaps you can trap the change event and then manually move the caret. If you think about it, caret position is strictly a view concern; it has nothing to do with converters or the data. You should not burden view-controller logic with it. You should definitely not burden converter logic (which is classified under utility classes) with it.

Related

WPF TextBlock MultiBinding

I want to bind TextBlock's Text property to some elements' and some model's properties. Something like this:
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<Binding ElementName="myElement1" Mode="OneWay" Path="Text" />
<Binding ElementName="myElement2" Mode="OneWay" Path="Text" />
<Binding Mode="OneWay" Path="Property1" />
<Binding Mode="OneWay" Path="Property2" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The TextBlock has a text value, combination of myElement1, myElement2 and Property1, Property2. There is not a problem. The text value is generated successfully.
Here is my question:
Can I bind whole (combined) text value of the TextBlock to another model's property, i.e. Property3, without code?
Not without some really bad hacking which would require writing some code to set up attached properties and other bindings anyway. The issue is that any binding has 2 ends: target and source. Since the target (where the binding is set) must be a DependencyProperty that means that your model must be on the source end of the binding you're trying to do. This isn't a problem as far as setting a value since TwoWay and OneWayToSource bindings do this just fine.
You have a bigger problem though in that the original place where the value is coming from (TextBlock.Text) already is assigned a binding and so can't be the target for your model binding. You might next want to try using another UIElement property as an intermediary to take the Text value and push it to the model. To do that you again need the model to be the source and the other UIElement property to be the target. But that same property also needs to be the target of a binding to the original Text property that you're trying to extract, so again you're stuck.
Bottom line is that you're much better off handling this in your Model and ViewModel layers rather than trying to force the stuff you have set up in XAML to be driving everything.

MultiBinding not working but corresponding Binding does work

I have the following code:
<local:StaffAtMeetingEditor DataContext="{Binding Meeting}" Grid.Row="1">
<local:StaffAtMeetingEditor.InEditMode>
<MultiBinding Converter="{StaticResource myMeetingLogEditableMultiConverter}">
<Binding Path="ParentSI.ItemInEditMode"/>
</MultiBinding>
</local:StaffAtMeetingEditor.InEditMode>
</local:StaffAtMeetingEditor>
The setup is that the containing control's datatype is "SIP_ServiceItem". This class has a property called "Meeting" (which is set as the DataContext for the local:StaffAtMeetingEditor control), which itself has a member called "ParentSI", pointing back to the parent SIP_ServiceItem object.
The issue is, that if I pass this through as a single binding (i.e. remove the start and end MultiBinding tags from the code above, leaving just the Binding), it works just fine. But when I make it a MultiBinding (I wish to add some other Bindings to this shortly), and try to pass the bound value through to myMeetingLogEditableMultiConverter, the values(0) parameter, which should correspond to the boolean ParentSI.ItemInEditMode is actually an MS.Internal.NamedObject, implying there's a null reference somewhere. Furthermore, the ParentSI property is never being evaluated, so something is going completely wrong. I am at a loss to know the difference between the single binding and multi binding cases.
Thanks.
I know this is a bit old, and you have likely figured this out by now, but I came across this as I had a similar problem and thought I'd share the solution: I had the same problem and have added the attributes ElementName and Mode as below:
<Binding Path="CurrentProvider.IsBusy" ElementName="parent" Mode="OneWay" />
Hope this helps someone, even if the OP has fixed their issue.
May be you should try to add any temporary unused bound value. For instance:
<local:StaffAtMeetingEditor DataContext="{Binding Meeting}" Grid.Row="1">
<local:StaffAtMeetingEditor.InEditMode>
<MultiBinding Converter="{StaticResource myMeetingLogEditableMultiConverter}">
<Binding Path="ParentSI.ItemInEditMode"/>
<Binding Path="ParentSI"/>
</MultiBinding>
</local:StaffAtMeetingEditor.InEditMode>
</local:StaffAtMeetingEditor>
If it doesn't work then your implementation is wrong, another case - it's MultiBinding limitations.

Why does my IMultiBindingConverter get an array of strings when used to set TextBox.Text?

I'm trying to use a MultiBinding with a converter where the child elements also have a converter.
The XAML looks like so:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource localizedMessageConverter}" ConverterParameter="{x:Static res:Resources.RecordsFound}" >
<Binding Converter="{StaticResource localizedMessageParameterConverter}" ConverterParameter="ALIAS" Path="Alias" Mode="OneWay" />
<Binding Converter="{StaticResource localizedMessageParameterConverter}" ConverterParameter="COUNT" Path="Count" Mode="OneWay" />
</MultiBinding>
</TextBlock.Text>
The problem I'm facing here is, whenever this is used with a TextBlock to specify the Text property, my IMultiValueConverter implementation gets an object collection of strings instead of the class returned by the IValueConverter. It seems that the ToString() method is called on the result of the inner converter and passed to the IMultiValueConverter. If used to specify the Content property of Label, all is well.
It seems to me that the framework is assuming that the return type will be string, but why? I can see this for the MultiBinding since it should yield a result that is compatible with TextBlock.Text, but why would this also be the case for the Bindings inside a MultiBinding?
If I remove the converter from the inner Binding elements, the native types are returned. In my case string and int.
Probably the targetType parameter of your localizedMessageParameterConverter converter is System.String. This is because the target type of the Bindings is inherited from the MultiBinding, and the targetType of the MultiBinding is System.String because TextBlock.Text is a string property.
See the following article for a similar problem: Multi-Value Converters, Value Converters and the Case of the Wrong Target Type
According to Microsoft Connect, this has been fixed in WPF 4.0. See: Microsoft Connect
The above article also explains a workaround.

set button datacontext to multiple elements

I've got a button that I need to be disabled when validation errors occur in my window. The items on which these errors can occur are all textboxes.
I've bound my Button's datacontext as such:
DataContext="{Binding ElementName=txtEmail}"
Now with this, I can set the button style to disabled when validation errors occur in the email textbox, but I want to do it also when it occurs in other textboxes in my window?
How can I set this binding to multiple textboxes?
You can't, at least not directly. You could use a MultiBinding with all of the desired text boxes as inputs, but you will need to provide an IMultiValueConverter to "combine" the various text boxes into one object (such as a list):
<Button>
<Button.DataContext>
<MultiBinding Converter="{StaticResource ListMaker}">
<Binding ElementName="txtEmail" />
<Binding ElementName="txtFirstName" />
<Binding ElementName="txtLastName" />
</MultiBinding>
</Button.DataContext>
</Button>
And it is then that resulting list object that will be passed to your trigger, so you won't be able to access the Validation.HasError property directly: your DataTrigger will also need to bring in a converter which converts the list object into a boolean indicating whether Validation.HasError is set for anything in the list. At this point you might as well just forget about triggers and bind IsEnabled using a MultiBinding:
<Button>
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource AllFalse}">
<Binding Path="(Validation.HasError)" ElementName="txtEmail" />
<Binding Path="(Validation.HasError)" ElementName="txtFirstName" />
<Binding Path="(Validation.HasError)" ElementName="txtLastName" />
</MultiBinding>
</Button.DataContext>
</Button>
(Here the AllFalse converter returns true if all inputs are false, and false if any input is true.)
A better approach, however, may be, instead of binding the Button directly to other UI elements, have your data object -- the same object that your text boxes are binding to -- expose an IsValid property (with suitable change notifications), and bind your Button.IsEnabled to that:
<Button IsEnabled="{Binding IsValid}" />
This moves you towards a MVVM-style solution which helps with things like testability (e.g. it's easy to create tests for the IsValid property; it's much harder to create tests for Button.IsEnabled).
For the MVVM approach you could try implementing a command router from ICommand.
<Button Command="{Binding Path=Commands.MyButtonCommand}" Style="{StaticResource MyButtonStyle}" ></Button>
where the Commands property is part of the ViewModel. You then have control over what functionality the command implements as well as whether it is enabled or not. Testing is then a whole lot easier.

How to determine WPF SelectedItem for a window

I have a WPF app with many list based controls in a window, which all are bound to different CollectionViews.
At the window level is there a way to get the current selected item for the currently in focus list based control? I know I can do this with some fairly trivial code by looking for the in focus element but does WPF support this as a concept out of the box?
Something like Window.CurrentSelectedDataItem would be great. I am looking into using this as a way to centralize command management for enabling disabling commands based on a current selected data item.
I don't think that there is a property like you specify, but as an alternative you could register a ClassHandler for the ListBox.SelectionChanged event in your Window class:
EventManager.RegisterClassHandler(typeof(ListBox), ListBox.SelectionChanged,
new SelectionChangedEventHandler(this.OnListBoxSelectionChanged));
This will get called whenever the selection changes in any ListBox in your application. You can use the sender argument to determine which ListBox it was that changed its selection, and cache this value for when you need it.
I haven't tried this, but you could try using a MultiBinding with a converter to get to the correct item:
<MultiBinding Converter="{StaticResource coalesce}">
<MultiBinding.Bindings>
<MultiBinding Converter="{StaticResource nullIfFalse}">
<MultiBinding.Bindings>
<Binding ElementName="List1" Path="HasFocus" />
<Binding ElementName="List1" Path="SelectedItem" />
nullIfFalse returns the second parameter, if the first is true, null otherwise. coalesce returns the first non-null element.

Resources