How to determine WPF SelectedItem for a window - wpf

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.

Related

Explicitly setting/ignoring visibility at design-time in VS2013?

I'm designing a form for a complex backing model in WPF. Some of the form controls rely on multiple options being set on the underlying view model, so I've set up for example
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource AndMultiValueVisibilityConverter}">
<Binding Path="RelevantSystemOption" />
<Binding Path="RelevantLicenseKeyOption"/>
</MultiBinding>
</TextBlock.Visibility>
AndMultiValueVisibilityConverter takes booleans and only makes something visible if they're all true, for reference.
This turns the control's visibility off at design time, which I don't want.
I'm aware of the ability of Expression Blend and design-time attributes to make certain layout determinations that can be ignored at run-time. d:IsHidden isn't being respected, and d:LayoutOverrides doesn't work on Visibility since it's a dynamic property.
I'd rather not dummy up an entire backing model with d:DataContext. I will if I have to, but is there an easier way to just force this particular control and maybe a handful of others to always be visible at design-time?
Just use
DesignerProperties.GetIsInDesignMode(new DependencyObject())
in your multivalue converter to determine if you're in the designer and, if so, return true.
It's okay to use this in a converter in an MVVM application, if you think it might go against the pattern. The converter is a UI concern.
https://msdn.microsoft.com/en-us/library/system.componentmodel.designerproperties(v=vs.110).aspx

WPF DataGrid validation when bound to a DataView

I am attempting to validate input on a DataGrid which is populated by a DataView (e.g. myDataGrid.ItemsSource = myDataView). However, all the WPF DataGrid validation examples I have seen assume that the DataGrid source is a C# class. I can't figure out how to hook up a single cell (i.e. a column) to a code-behind validation. Can someone give an example or point me to one?
Ok, I did some quick tests; I think the main impediment to really doing it manually is that the DataGridCell doesn't create any bindings for its content when assigning directly a DataView for display. However, if you're willing to jump through the hoops of assigning custom DataGridTemplateColumns when creating the DataGrid, you can access the bindings on, say, the TextBox instances inside the template, and set custom validation errors on them.
Well actually, either this or override the default style of the textboxes generated by the DataGrid; you see, the default textbox style doesn't have any borders or anything, so setting a validation error on it doesn't have any effect. Making it into a normal textbox with DataTemplate or overriding its style would enable you to actually make something visible as an effect of setting the validation error.
However you'll need to do some research by yourself; I did a quick prototype and it works, but I can't create the binding correctly in the DataTemplate (either I forgot all the WPF I knew since I worked with it last, or I only know how to work with proper bindings :)). If you get somewhere with that it's easier going forward:
Somehow get access to a BindingExpression; what I did was attach a handler to the LostFocus event and checking if what lost focus was a textbox that was inside a DataGridCell; if so, I simply mark that binding as invalid (with t representing a TextBox instance):
var x = t.GetBindingExpression(TextBox.TextProperty);
Validation.MarkInvalid(x, new ValidationError(new ExceptionValidationRule(), x.ParentBinding, "error", new Exception("error")));
I must confess I'm not sure anymore what each argument's role is in the ValidationError constructor is (I think the exception message is displayed by default in the validation tooltip? Or is it the error content?), but I'm sure you can figure it out. Now if you don't mind, I think I'll take a break... like I said, it's not easy going against the flow :)
So I did some more research, and what I was basically missing was that I can specify the column name with the Path attribute of a Binding (or even use the column ordinal in brackets, e.g. Path="[0]"). After that realization, everything is basically the same as using a class property. So a typical DataGrid column definition I use follows:
<DataGridTextColumn Header="Regular" EditingElementStyle="{StaticResource ValidationErrorStyleBoxRA}" ElementStyle="{StaticResource ValidationErrorStyleBlockRA}" Width="60">
<DataGridTextColumn.Binding>
<Binding Path="HourlyRate" StringFormat="F3" ValidatesOnExceptions="True" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:HourlyRatesAmountValidate ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>

How can I suppress a ListView from displaying when content when empty

I have an object that contains an IEnumerable called ValidPhones as a property. When I bind it to a WPF ListView like such:
<ListView DataContext="{Binding ValidPhones}">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat='{}{0}: {1}'>
<Binding Path='Type' />
<Binding Path='PhoneNumber' />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ListView>
It still ends up displaying the ": " in the UI even when the Enumerable is empty. Is there a simple way to suppress that from happening?
What backend language are you using with wpf? I'm fairly sure it's either c# or VB.NET. I'm no expert, but I'm pretty sure you can access things like ListViews from code.
EDIT: sorry...I missed the comment at the top. Please mark your threads as closed when you solve the problem so silly people like me don't post useless answers.
Sure! In fact, there are a few options for you to consider.
Option 1: You can always change the visibility of your control to "collapsed" when the list is empty, which will keep your application from rendering it in the xaml. This can be done from your C# code or your xaml. this is a viable solution, but be careful that you don't have other controls that are dependant upon the location of the listview, because setting the visibility to "collapsed" will tell your applicaitonPage not to render the listview at all, which can mess with your layout.
Option 2: You can set up an "IsEmpty" attribute for your iEnumerable, and then bind that attribute to the textblock's text property. If you do it this way then you will need a converter that can return either a blank string or the properly formatted one depending on the value of IEnumerable. This opiton is slightly more complicated, but it has the benefit of leaving your listview alone so that the other controls don't move around.
Please let me know if you need help with either of these options, or if they need clarificaiton :)

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

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.

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.

Resources