Why can't I select a null value in a ComboBox? - wpf

In WPF, it seems to be impossible to select (with the mouse) a "null" value from a ComboBox. Edit To clarify, this is .NET 3.5 SP1.
Here's some code to show what I mean. First, the C# declarations:
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar
{
public string Name { get; set; }
}
Next, my Window1 XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox x:Name="bars"
DisplayMemberPath="Name"
Height="21"
SelectedItem="{Binding Bar}"
/>
</StackPanel>
</Window>
And lastly, my Window1 class:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
bars.ItemsSource = new ObservableCollection<Bar>
{
null,
new Bar { Name = "Hello" },
new Bar { Name = "World" }
};
this.DataContext = new Foo();
}
}
With me? I have a ComboBox whose items are bound to a list of Bar instances, one of which is null. I have bound the window to an instance of Foo, and the ComboBox is displaying the value of its Bar property.
When I run this app, the ComboBox starts with an empty display because Foo.Bar is null by default. That's fine. If I use the mouse to drop the ComboBox down and select the "Hello" item, that works too. But then if I try to re-select the empty item at the top of the list, the ComboBox closes and returns to its previous value of "Hello"!
Selecting the null value with the arrow keys works as expected, and setting it programatically works too. It's only selecting with a mouse that doesn't work.
I know an easy workaround is to have an instance of Bar that represents null and run it through an IValueConverter, but can someone explain why selecting null with the mouse doesn't work in WPF's ComboBox?

Well I recently ran into the same problem with null value for ComboBox.
I've solved it by using two converters:
For ItemsSource property: it replaces null values in the collection by any value passed inside converter's parameter:
class EnumerableNullReplaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var collection = (IEnumerable)value;
return
collection
.Cast<object>()
.Select(x => x ?? parameter)
.ToArray();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
For SelectedValue property: this one does the same but for the single value and in two ways:
class NullReplaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value ?? parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter) ? null : value;
}
}
Example of use:
<ComboBox
ItemsSource="{Binding MyValues, Converter={StaticResource EnumerableNullReplaceConverter}, ConverterParameter='(Empty)'}"
SelectedValue="{Binding SelectedMyValue, Converter={StaticResource NullReplaceConverter}, ConverterParameter='(Empty)'}"
/>
Result:
Note:
If you bind to ObservableCollection then you will lose change notifications. Also you don't want to have more than one null value in the collection.

The null "item" is not being selected by the keyboard at all - rather the previous item is being unselected and no subsequent item is (able to be) selected. This is why, after "selecting" the null item with the keyboard, you are thereafter unable to re-select the previously selected item ("Hello") - except via the mouse!
In short, you can neither select nor deselect a null item in a ComboBox. When you think you are doing so, you are rather deselecting or selecting the previous or a new item.
This can perhaps best be seen by adding a background to the items in the ComboBox. You will notice the colored background in the ComboBox when you select "Hello", but when you deselect it via the keyboard, the background color disappears. We know this is not the null item, because the null item actually has the background color when we drop the list down via the mouse!
The following XAML, modified from that in the original question, will put a LightBlue background behind the items so you can see this behavior.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Background="LightBlue" Width="200" Height="20">
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
If you want further validation, you can handle the SelectionChanged event on the ComboBox and see that "selecting the null item" actually gives an empty array of AddedItems in its SelectionChangedEventArgs, and "deselecting the null item by selecting 'Hello' with the mouse" gives an empty array of RemovedItems.

I got a new solution for this question. "USING Mahapps"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
<ComboBox x:Name="bars" **controls:TextBoxHelper.ClearTextButton="True"**
DisplayMemberPath="Name"
Height="21"
SelectedItem="{Binding Bar}"/>
You can use the close button to clear the content.
Thanks.

I know this answer isn't what you asked for (an explanation of why it doesn't work with the mouse), but I think the premise is flawed:
From my perspective as a programmer and user (not .NET), selecting a null value is a bad thing. "null" is supposed to be the absence of a value, not something you select.
If you need the ability explicitly not to select something, I would suggest either the work-around you mentioned ("-", "n.a." or "none" as a value), or better
wrap the combobox with a checkbox that can be unchecked to disable the combobox. This strikes me as the cleanest design both from a user's perspective and programmatically.

I spent one day to find a solution about this problem of selecting a null value in combobox and finally, yeah finally I found a solution in an article written at this url:
http://remyblok.tweakblogs.net/blog/7237/wpf-combo-box-with-empty-item-using-net-4-dynamic-objects.html
public class ComboBoxEmptyItemConverter : IValueConverter
{
/// <summary>
/// this object is the empty item in the combobox. A dynamic object that
/// returns null for all property request.
/// </summary>
private class EmptyItem : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// just set the result to null and return true
result = null;
return true;
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// assume that the value at least inherits from IEnumerable
// otherwise we cannot use it.
IEnumerable container = value as IEnumerable;
if (container != null)
{
// everything inherits from object, so we can safely create a generic IEnumerable
IEnumerable<object> genericContainer = container.OfType<object>();
// create an array with a single EmptyItem object that serves to show en empty line
IEnumerable<object> emptyItem = new object[] { new EmptyItem() };
// use Linq to concatenate the two enumerable
return emptyItem.Concat(genericContainer);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<ComboBox ItemsSource="{Binding TestObjectCollection, Converter={StaticResource ComboBoxEmptyItemConverter}}"
SelectedValue="{Binding SelectedID}"
SelectedValuePath="ID"
DisplayMemberPath="Name" />

this might not address your answer completely, but hopefully its a hit in the right direction:
Have you installed SP1?
From Scott Gu's Blog:
NET 3.5 SP1 includes several data binding and editing improvements to
WPF. These include:
StringFormat support within {{ Binding }} expressions to enable easy
formatting of bound values
New alternating rows support within controls derived
from ItemsControl, which makes
it easier to set alternating properties on rows (for example: alternating background colors)
Better handling and conversion support for null values
in editable controls Item-level
validation that applies validation rules to an entire bound item
MultiSelector support to handle multi-selection and bulk
editing scenarios
IEditableCollectionView support to interface data controls
to data sources and enable editing/adding/removing items in a transactional way
Performance improvements when binding to IEnumerable data
sources
Sorry if I wasted your time and this was not even close..but I think the problem is inherited from:
constraints of the strongly typed dataset
NullValueDataSet Explained here
But now the SP1 for .Net 3.5 should have addressed this issue..

I had the same kind of problem we did some work around like adding a value property to the collection item like this :
public class Bar
{
public string Name { get; set; }
public Bar Value
{
get { return String.IsNullOrEmpty(Name) ? null : this; } // you can define here your criteria for being null
}
}
Then while adding items instead of null I use the same object :
comboBox1.ItemsSource= new ObservableCollection<Bar>
{
new Bar(),
new Bar { Name = "Hello" },
new Bar { Name = "World" }
};
And instead of selecteditem I bind it to selectedvalue :
<ComboBox Height="23" Margin="25,40,133,0" DisplayMemberPath="Name"
SelectedValuePath="Value"
SelectedValue="{Binding Bar}"
Name="comboBox1" VerticalAlignment="Top" />
I know It is not a complete solution, just one workaround I use

Try Binding.FallbackValue
From 6 Things I Bet You Didn't Know About Data Binding in WPF

ComboBox needs a DataTemplate to display the item no matter how simple it is.
DataTemplate works like this: get a value from instance.[path], e.g.
bar1.Car.Color
So it cannot get a value from
null.Car.Color
It will throw a null reference exception. So, the null instance will not be displayed. But the the Color - if it is a reference type - is allowed to be null because there will be no exception in this case.

Just a guess, but I think it sounds reasonable.
Assume combobox is using "ListCollectionView" (lcv as its instance) as its item collection, which it should be.
If you are a programmer, what you gonna do?
I will respons to both Keyboard and Mouse.
Once I get Keyboard input, I use
lcv.MoveCurrentToNext();
or
lcv.MoveCurrentToPrevious();
So, sure keyboard works well.
Then I am working on respons Mouse inputs. And it comes the problem.
I want to listen 'MouseClick' event of my item. But probably, my Item doesn't generated, it is just a placeholder. So when user click on this placeholder, I get nothing.
If I get the event successfully, what's next. I will invoke
lcv.MoveCurrentTo(selectedItem);
the "selectedItem" which would be null is not an acceptable parameter here I think.
Anyway, it's just guessing. I don't have time to debug into it though I am able to. I have a bunch of defects to fix. Good Luck. :)

Related

UWP subclassed ComboBox control in C# for enum - Display

I've developed a subclassed ComboBox control in C#, UWP, for enum type properties.
It works great! Almost all the time (... types).
Problem surfaced when the type of the enum was Windows.UI.Text.FontStyle.
The item selection still works right, but what it displays is not the .ToString() values, but Windows.Foundation.IReference`1<Windows.UI.Text.FontStyle> for each item.
When I debug, everything is the same and fine as far as my code is concerned.
My control works by a DependencyProperty called SelectedItemEnum - SelectedItemEnumProperty, which' type is object.
And by this binded concrete enum value it sets the ItemsSource:
ItemsSource = Enum.GetValues(SelectedItemEnum.GetType()).Cast<Enum>().ToList();
(And I handle the SelectionChanged event (inside the control) to set the value, but that part always works well.)
I have now been trying this for an hour or so, but I am unable to figure out why this happens and I would definitely be interested to see the real reason behind this.. Apparently there is something about the FontStyle enum, that causes it to get represented as nullable (IReference seems to be "equivalent" to nullable in .NET world).
To solve your problem however, you can build a custom ValueConverter, that will convert the value to string before displaying.
First create a ToStringConverter:
public class ToStringConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, string language )
{
var stringValue = value.ToString();
return stringValue;
}
public object ConvertBack( object value, Type targetType, object parameter, string language )
{
throw new NotImplementedException();
}
}
Now add is as a resource to your page or to the app itself:
<Page.Resources>
<local:ToStringConverter x:Key="ToStringConverter" />
</Page.Resources>
You can then use it with the combo box as follows:
<local:EnumComboBox x:Name="EnumComboBox">
<local:EnumComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource ToStringConverter}}" />
</DataTemplate>
</local:EnumComboBox.ItemTemplate>
</local:EnumComboBox>
This will correctly display the value of the enum. You can see it and try it out in here on my GitHub, along with the sample app I have used to try to figure this out.
I will keep trying to find the reason however, as it does really interest me :-) .

Can you replace characters in a textbox as you type?

We are looking for a way to make a textbox replace certain characters as a person types in it. Note, we are focusing on the control itself, not bindings, viewmodels, etc. For the sake of this question, assume there is a window with a textbox sitting in the middle of it and nothing else. No data, no viewmodel, no bindings, etc.
I've updated the question because it seems all the answers below keep focusing on bindings, dependency properties, coersion, etc. While I appreciate the suggestions, as I stated above, our control is not a bound control so they aren't applicable.
Now while I could explain the reasons for that, that would make this post about five times longer as it's actually a complex and advanced use-case, but that has nothing to do with the question itself, which is why I've simplified our scenario down to focus on the specific question we're trying to resolve, which is about a textbox control, or possibly a subclass of one replacing characters as you type.
Hope that makes more sense now.
The best way to accomplish this is using the TextChanged event:
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
var tb = (TextBox)sender;
using (tb.DeclareChangeBlock())
{
foreach (var c in e.Changes)
{
if (c.AddedLength == 0) continue;
tb.Select(c.Offset, c.AddedLength);
if (tb.SelectedText.Contains(' '))
{
tb.SelectedText = tb.SelectedText.Replace(' ', '_');
}
tb.Select(c.Offset + c.AddedLength, 0);
}
}
}
This has several advantages:
You don't go through the entire string every time, just the replaced part
It behaves well with the undo manager and with pasting text
You can easily encapsulate it into an attached property which can be applied to any text box
You can use an IValueConverter. It involves quite a bit of code, but it's the preferred way if you ever want to go the MVVM path, or just do things the WPF way.
First, create a class that implements IValueConverter
public class IllegalCharactersToUnderscoreConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var stringValue = value as String;
if(stringValue == null)
return value;
return RemoveIllegalCharacters(stringValue);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Import the namespace containing your ValueConverter
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
Create an instance of your converter in Window.Resources
<Window.Resources>
<local:IllegalCharactersToUnderscoreConverter x:Key="IllegalCharactersToUnderscore" />
</Window.Resources>
Use the converter in your binding expression. UpdateSourceTrigger=PropertyChanged is required for the text to be converted as you type.
<TextBox Text="{Binding MyText,
Converter={StaticResource IllegalCharactersToUnderscore},
UpdateSourceTrigger=PropertyChanged}"/>
You could subclass TextBox you can just override the Metadata of the TextBox Text property
public class FilteredTextBox : TextBox
{
public FilteredTextBox()
{
TextBox.TextProperty.OverrideMetadata(typeof(FilteredTextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceMyTextValue, true, UpdateSourceTrigger.PropertyChanged));
}
private static object CoerceMyTextValue(DependencyObject d, object baseValue)
{
if (baseValue != null)
{
var userEnteredString = baseValue.ToString();
return userEnteredString.Replace(' ', '_');
}
return baseValue;
}
}
And you dont need to use bindings at all, this will just update the internal TextBox TextProperty as you type
<Window x:Class="WpfApplication13.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="428" Width="738" Name="UI"
xmlns:my="clr-namespace:WpfApplication13" >
<StackPanel>
<my:FilteredTextBox />
</StackPanel>
</Window>
I tried to achieve this thing by handling this in my ViewModel property that is bind to TextBox itself like below and it works. In this example I replace '!' with underscore. Here i just put the replacement logic in property setter and if there is a change I replaced the text and Raised the propertychange async.
private string text;
public string Text
{
get { return text; }
set
{
text = value;
if (text.Contains("!"))
{
text = text.Replace('!', '_');
Dispatcher.BeginInvoke((Action) (() => RaisePropertyChange("Text")));
}
RaisePropertyChange("Text");
}
}
and the textbox binding is
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>

Selecting Current MenuItem in wpf

How can I select the currentItem in a menuitem collection. Like one would do with a listbox. I tried wrapping the collection in a collectionViewSource, However that brought be no such luck.
Thanks in advance.
The MenuBase, which ContextMenu and Menu derive from, inherits ItemsControl, which does not include the concept of SelectedItem. That's something that ListBox adds.
You do, however, have the ItemsControl.ItemTemplate. Which is awesome.
One option would be to make your ItemTemplate a ToggleButton. This gives you a couple of things. Inherently, ToggleButtons can look like they're selected using their IsChecked property. Second, they have a Command property which you can bind to a command in your ViewModel.
So, if you have something along the lines of:
<Menu ItemsSource="{Binding ThingsToBindTo}">
<Menu.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<conv:BindingProxy x:Key="proxy" Data="{Binding}" />
</Grid.Resources>
<ToggleButton Content="{Binding NameOrLabel}" CommandParameter="{Binding}" Command="{Binding Path=DataContext.SelectThingCommand, RelativeSource={RelativeSource AncestorType=Menu}}" >
<ToggleButton.IsChecked>
<Binding Mode="OneWay" Path="DataContext.SelectedThing" RelativeSource={RelativeSource AncestorType=Menu}">
<Binding.Converter>
<conv:ComparisonConverter CompareTo="{Binding Source={StaticResource proxy}, Path=Data}" />
</Binding.Converter>
</Binding>
</ToggleButton.IsChecked>
</ToggleButton>
</Grid>
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
So this is a little complicated.
As per normal, you're binding to a list of items. ThingsToBindTo should be whatever your list is. Then you start defining your template. NameOrLabel is whatever property you want to appear on your toggle button. The command parameter is binding to the data item that the template is wrapping around by using nothing more than "{Binding}". The command is actually on the DataContext of your Menu, which is why RelativeSource is used here.
What this is saying is you're going to pass a command the thing that was just clicked. Effectively, you're selecting the button you click. Then, your command just needs to set a SelectedThing property in your ViewModel equal to whatever Thing is passed to it. Hopefully you have implemented a class that implements ICommand to create your delegate commands. If you don't, there are a lot of articles out there on how to do it. If you don't know how, put a comment on this post and I'll add the source code to do it.
Then we have the "IsChecked" bad boy. We're actually doing a binding long-hand there. This is the more complicated piece, but it allows a DataTemplated item to actually bind to itself within a converter.
First, you need the proxy object, which is explained here:
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
Very simple to implement. Once it is done, the BindingProxy resource within your grid will work, and can act as an anchor back to the item bound to by the DataTemplate. The linked article explains why.
Then, you need a converter that compares two objects to each other.
public class ComparisonConverter : DependencyObject, IValueConverter
{
public object CompareTo
{
get { return (object)GetValue(CompareToProperty); }
set { SetValue(CompareToProperty, value); }
}
public static readonly DependencyProperty CompareToProperty =
DependencyProperty.Register("CompareTo", typeof(object), typeof(ComparisonConverter), new UIPropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (CompareTo != null)
{
return CompareTo.Equals(value);
}
else
{
return false;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
So now that binding will take the selected item from the DataContext of the menu, and compare it to whatever the ToggleButton is bound to. If the two objects match, the button appears clicked/selected. If they don't match, the button doesn't look selected.
So I do happen to have that BindingProxy and my converter in the same namespace. You don't necessarily have to do that. I just usually have a namespace for "Xaml Trick" classes that I have to program.
This is a lot to digest, and I'm happy to clarify anything.
One other thing...if you don't like the "ToggleButton" look, you can always style them to look completely different. The thing that having a ToggleButton buys you is the "IsChecked" property and the Command property. You can make the ContentTemplate look like anything you want, which gives you a lot of freedom in styling your menu.
If the ListBox has its ItemsSource set to a generic list of a complex entity, using ListBox.SelectedValue will get you the currently selected data.
For example:
public partial class NameListView : Window
{
/// <summary>
/// Constructor
/// </summary>
public NameListView()
{
List<string> names = new List<string>();
names.Add("John Doe");
names.Add("Jane Doe");
lbNameList.ItemsSource = names;
}
/// <summary>
/// Selection changed event handler for ListBox lbNameList
/// </summary>
void lbNameList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
string currentValue = lbNameList.SelectedValue.ToString();
MessageBox.Show("Currently selected value: " + currentValue);
}
}
}
If you have a property in your contextfile ( like Codebehind file or ViewModel ) that represents the currentSelectedItem then you can write the following in your xaml :
<ListView x:Name="MyList"
ItemsSource="MySource"
SelectedItem="{Binding Path=MyCurrentSelectedItem}" IsSynchronizedWithCurrentItem="True">
Codebehind / ViewModel
public MyType MyCurrentSelectedItem { get; set; }

Select the "old value" in a listbox in a edit page

In my app I have a Save / Edit page.
The current flow is the following: the user has a main page, with a list of elements. He can click on the "add" button, it goes to an "Add" page, in which he can enter information and store it. Once he does it, the information is saved and shown in the list.
If he clicks in the list, he moves to an "Edit" page, in which he can change the information.
In reality, the Add and the Edit page are the same, the second has the fields populated while the first hasn't.
I have 3 listboxes in this page, one for severities, one for categories and one for reporter. This information is selected in the listbox before saving, and in the edit phase, it should be selected automatically, so the user knows the "old" value.
To select the values automatically, I tried two approaches:
1-In my xaml:
<ListBox Height="103" Name="lbSeverities" Width="439" HorizontalAlignment="Left" Margin="20,0,0,0" SelectionMode="Single" ItemsSource="{Binding Severities}" DisplayMemberPath="Name" SelectedItem="{Binding Task.Severity}"/>
And I also override the Equals method of the Severity Class to a reasonable implementation.
2- In my xaml
<ListBox Height="103" Name="lbSeverities" Width="439" HorizontalAlignment="Left" Margin="20,0,0,0" SelectionMode="Single" ItemsSource="{Binding Severities}" DisplayMemberPath="Name" SelectedIndex="{Binding Task.Severity, Converter={StaticResource SeverityToIndexConverter}}"/>
And I created the SeverityToIndexConverter with this code:
public class SeverityToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is Severity)
{
Severity currentSeverity = (Severity)value;
for (int i = 0; i < (App.Current as App).MainViewModel.Severities.Count; i++)
{
Severity sev = (App.Current as App).MainViewModel.Severities[i];
if (currentSeverity.ID == sev.ID)
return i;
}
}
return -1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
Both of them presented the same results: sometimes the values are automatically selected, but sometimes not. It is very unstable when it is selected.
I imagined about some exception, tried to catch it, but I do not get anything.
I also tried to debug, I noticed that in case 1, the equals method is called in parallel for all members of the collection, so I tried the second approach. Debugging didn't lead me to any answer.
Has anyone faced a similar situation?
What can I do to make the Listbox value to be selected when the user enters in the "Edit" Page?
Thanks,
Oscar
I do the same thing. My xaml looks like your first example.
Here's what I'd do:
Create a new property called Severity. In the getter return Task.Severity. Bind the listbox SelectedItem to the new property.
It may also be a timing thing so you may have to call NotifyPropertyChanged on the new property once the listbox has loaded its list.

WPF bind IsEnabled on other controls if a listbox has a select item

I have a grid with 2 columns, a listbox in column 0 and a number of other controls in the a secondary grid in the main grids column 1.
I want this controls only to be enabled (or perhaps visible) if an items is selected in the listbox through binding. I tried on a combo box:
IsEnabled="{Binding myList.SelectedIndex}"
But that does not seem to work.
Am I missing something? Should something like this work?
thanks
You'll need a ValueConverter for this. This article describes it in detail, but the summary is you need a public class that implements IValueConverter. In the Convert() method, you could do something like this:
if(!(value is int)) return false;
if(value == -1) return false;
return true;
Now, in your XAML, you need to do:
<Window.Resources>
<local:YourValueConverter x:Key="MyValueConverter">
</Window.Resources>
And finally, modify your binding to:
IsEnabled="{Binding myList.SelectedIndex, Converter={StaticResource MyValueConverter}"
Are you sure you didn't mean
IsEnabled="{Binding ElementName=myList, Path=SelectedIndex, Converter={StaticResource MyValueConverter}"
though? You can't implicitly put the element's name in the path (unless the Window itself is the DataContext, I guess). It might also be easier to bind to SelectedItem and check for not null, but that's really just preference.
Oh, and if you're not familiar with alternate xmlns declarations, up at the top of your Window, add
xmlns:local=
and VS will prompt you for the various possibilities. You need to find the one that matches the namespace you put the valueconverter you made in.
Copy-paste solution:
Add this class to your code:
public class HasSelectedItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is int && ((int) value != -1);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add converter as StaticResource to App.xml in <Application.Resources> section:
<local:HasSelectedItemConverter x:Key="HasSelectedItemConverter" />
And now you can use it in your XAML:
<Button IsEnabled="{Binding ElementName=listView1, Path=SelectedIndex,
Converter={StaticResource HasSelectedItemConverter}"/>
Hmm, perhaps it works with a BindingConverter, which converts explicitly all indexes > 0 to true.

Resources