Selecting Current MenuItem in wpf - 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; }

Related

In WPF how do I properly bind the style of a button within an ItemsControl template and use a converter?

In WPF, I am trying to have the button created as part of the ItemTemplate in a ItemsControl select the style using a property within the class. I thought I had it coded correctly, but when I run this, it creates the button "Test", but it never even runs the converter for the Style.
By the way, yes, I'm obviously aware that I haven't properly implemented the IValueConverter yet, but when I set breakpoints, it never even enters the converter.
Also, how do I properly keep the style of the button updated when the value of the property CurrentItemProperty changes?
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ButtonStyleConverter x:Key="ButtonStyleConverter"/>
</Window.Resources>
<Grid>
<ItemsControl x:Name="ButtonList">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="OptionButton">
<Button Content="{Binding DisplayName}" Style="{Binding CurrentItem, Converter={StaticResource ButtonStyleConverter}}"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WpfApp1
{
public partial class MainWindow : Window
{
DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItemProperty", typeof(string), typeof(MainWindow));
public string CurrentItem
{
get
{
return (string)GetValue(CurrentItemProperty);
}
set
{
SetValue(CurrentItemProperty, value);
}
}
public MainWindow()
{
InitializeComponent();
ButtonList.ItemsSource = new OptionButton[]
{
new OptionButton() { DisplayName = "Test"}
};
CurrentItem = "Test";
}
public class OptionButton
{
public string DisplayName { get; set; }
}
}
public class ButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
when I set breakpoints, it never even enters the converter.
Because there's nothing to convert.
If you would run your code in the debugger and look at the debug output, you'd find that you're getting a binding error, where the binding engine fails to find the property CurrentItem on the source object. That's because the source object is a OptionButton instance, which of course does not have a CurrentItem property.
You have a number of options available to you:
Control the style some other way. There's not enough context in your question to know why you're trying to use a converter to return a style, but it's unusual enough an approach that I'll suggest this should be your first thing to try. There's probably a better way to control the styling of the button than using a converter this way.
Use a property that's on the OptionButton type. In your example, the CurrentItem and OptionButton.DisplayName have the same value, so taking the example literally you could just bind to the DisplayName property instead. Alternatively, maybe you could implement a property in OptionButton that delegates to the parent-level value you care about.
Set the binding source to the window, so that the CurrentItem property is visible. There are (at least) two variations on this theme: give the window a name attribute, and refer to it by name in the binding, or just use {RelativeSource AncestorType=Window} markup for the source of the binding.
The bottom line is that the binding doesn't work as expected because the current data context for the binding (which defines the default source for the binding) doesn't have the property you're binding to.

How to pass my view model to a user to main view model?

I made a main window that displays various user controls in a content control. In this window, I have the user controls and their accompanying view models in the XAML as DataTemplate Resources. This window has a button that needs to display the user control in the contentcontrol and instantiate the view model for it. How can i pass the resource to my RelayCommand, so that i can tell the command which user control and view model to use? I figured out how to pass a hard-coded string as the command parameter, but now I'm wanting to pass the x:Name so i can reuse this command etc for more than one View-ViewModel.
Main Window's XAML snippets:
<Window.Resources>
<!--User Controls and Accompanying View Models-->
<DataTemplate DataType="{x:Type EmployerSetupVM:EmployerSetupVM}" x:Key="EmployerSetup" x:Name="EmployerSetup">
<EmployerSetupView:EmployerSetupView />
</DataTemplate>
<DataTemplate DataType="{x:Type VendorSetupVM:VendorSetupVM}">
<VendorSetupView:VendorSetupView />
</DataTemplate>
</Window.Resources>
<Button Style="{StaticResource LinkButton}" Command="{Binding ShowCommand}" CommandParameter="{StaticResource EmployerSetup}">
...
In the Main Window's ViewModel, here relevant code so far:
public RelayCommand<DataTemplate> ShowCommand
{
get;
private set;
}
ShowCommand = new RelayCommand<string>((s) => ShowExecuted(s));
private void ShowExecuted(DataTemplate s)
{
var fred = (s.DataType); //how do i get the actual name here, i see it when i hover with intellisense, but i can't access it!
if (!PageViewModels.Contains(EmployerSetupVM))
{
EmployerSetupVM = new EmployerSetupVM();
PageViewModels.Add(EmployerSetupVM);
}
int i = PageViewModels.IndexOf(EmployerSetupVM);
ChangeViewModel(PageViewModels[i]);
}
...
in other words, how do i get the name of the my DataTemplate w/ x:Key="EmployerSetup" in the XAML? If it matters, I'm using MVVMLight too
Try using the Name property of the class Type:
private void ShowExecuted(DataTemplate s) {
var typeName = s.DataType as Type;
if (typeName == null)
return;
var className = typeName.Name; // className will be EmployerSetupVM or VendorSetupVM
...
}
I'd still say passing the DataTemplate to the VM just seems strange. I'd just have two commands and switch the command used in the Button.Style according to the conditions you got.
If you "have" to use a single RelayCommand or the world might end, I'd tend to use a static enum that you can reference from xaml for CommandParameter than pass the whole DataTemplate object.

DataGrid: dynamic DataTemplate for dynamic DataGridTemplateColumn

I want to show data in a datagrid where the data is a collection of
public class Thing
{
public string Foo { get; set; }
public string Bar { get; set; }
public List<Candidate> Candidates { get; set; }
}
public class Candidate
{
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
where the number of candidates in Candidates list varies at runtime.
Desired grid layout looks like this
Foo | Bar | Candidate 1 | Candidate 2 | ... | Candidate N
I'd like to have a DataTemplate for each Candidate as I plan changing it during runtime - user can choose what info about candidate is displayed in different columns (candidate is just an example, I have different object). That means I also want to change the column templates in runtime although this can be achieved by one big template and collapsing its parts.
I know about two ways how to achieve my goals (both quite similar):
Use AutoGeneratingColumn event and create Candidates columns
Add Columns manually
In both cases I need to load the DataTemplate from string with XamlReader. Before that I have to edit the string to change the binding to wanted Candidate.
Is there a better way how to create a DataGrid with unknown number of DataGridTemplateColumn?
Note: This question is based on dynamic datatemplate with valueconverter
Edit: As I need to support both WPF and Silverlight, I've created my own DataGrid component which has DependencyProperty for bindig a collection of columns. When the collection changes, I update the columns.
For example we create 2 DataTemplates and a ContentControl:
<DataTemplate DataType="{x:Type viewModel:VariantA}"> <dataGrid...> </DataTemplate>
<DataTemplate DataType="{x:Type viewModel:VariantB}"> <dataGrid...> </DataTemplate>
<ContentControl Content="{Binding Path=GridModel}" />
Now if you set your GridModel Property (for example type object) to VariantA or VariantB, it will switch the DataTemplate.
VariantA & B example Implementation:
public class VariantA
{
public ObservableCollection<ViewModel1> DataList { get; set; }
}
public class VariantB
{
public ObservableCollection<ViewModel2> DataList { get; set; }
}
Hope this helps.
I don't know if this is a "better" way, since this remains pretty ugly, but I personnaly did like this:
make the template in xaml
use a multibind that takes the current binding + a binding to the column to get the "correct" dataContext (i.e.: the cell instead of the row)
use a converter on this binding to get the value of the property you like, an optionally add a parameter if you have many properties to retrieve.
e.g.: (sorry, I did not adapt my code to suit your project, but you should be able to do it yourself from there)
here is my dataTemplate:
<DataTemplate x:Key="TreeCellTemplate">
<Grid>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource RowColumnToCellConverter}" ConverterParameter="Text">
<Binding />
<Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Column" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
and here is my converter:
public class RowColumnToCellConverter : MarkupExtension, IMultiValueConverter
{
public RowColumnToCellConverter() { }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
XwpfRow row = values[0] as XwpfRow;
XwpfTreeColumn column = values[1] as XwpfTreeColumn;
if (row == null || column == null) return DependencyProperty.UnsetValue;
TreeCell treeCell = (TreeCell)row[column.DataGrid.Columns.IndexOf(column)];
switch ((string)parameter)
{
case "Text": return treeCell.Text;
case "Expanded": return treeCell.Expanded;
case "ShowExpandSymbol": return treeCell.ShowExpandSymbol;
case "CurrentLevel": return new GridLength(treeCell.CurrentLevel * 14);
default:
throw new MissingMemberException("the property " + parameter.ToString() + " is not defined for the TreeCell object");
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new RowColumnToCellConverter();
}
}
this saves the MVVM model, and I prefer this way of doing things because I really dislike using xaml parsers to make "dynamic" datatemplates, but it's still an ugly Hack from my point of view.
I wish the guys at MS would give us a way to get cells instead of rows as dataContexts to be able to generate templated columns on the fly...
hope this helps
EDIT: In your case, the converter ought to be a lot simpler actually (you can return the cell's instance directly if I'm not mistaken, and you don't need any parameter), but I left the more complex version nonetheless, just in case somebody else has a similar issue
I've been looking at a similar problem and have only found a handful of useful patterns. The whole 'dynamic column' problem is an interesting one in silverlight.
Yesterday I found this page Silverlight DataGrid with Dynamic Columns on Travis Pettijohn's site during my searches.
Previously I'd been using the 'index converter' pattern outlined by Colin Eberhardt which works fantastically well... as long as you use DataGridTextColumn. Everything can be done in code behind, and I had no trouble applying styles at run time. However my requirement is now to apply some 'cell level' formatting - change the background for the cell, etc - which means a DataGridTemplateColumn is required.
The big problem with a DataGridTemplateColumn for me was that I can't set the binding in code. I know we can build it by parsing xaml, but like everyone else that seems like a massive hack and unmaintainable to the nth.
The pattern outlined by Travis (the first link above) is completely different. At 'run time' (i.e. page load time), create the columns you need in your grid. This means iterate through your collection, and add a column for each item with the appropriate header etc. Then implement a handler for the RowLoaded event, and when each row is loaded simply set the DataContext for each cell to the appropriate property / property index of the parent.
private void MyGrid_RowLoaded(object sender, EventArgs e)
{
var grid = sender as DataGrid;
var myItem = grid.SelectedItem as MyClass;
foreach (int i = 0; i < myItem.ColumnObjects.Count; i++)
{
var column = grid.Columns[i];
var cell = column.GetCellContent(e.Row)
cell.DataContext = myItem.ColumnObjects[i];
}
}
This has removed the need for me to use the index converter. You can probably use a Binding when setting the cell.DataContext but for me it's easier to have the template simply bind directly to the underlying object.
I now plan on having multiple templates (where each can bind to the same properties on my cell object) and switching between them at page load. Very tidy solution.

Can't bind ICommand in VM to button Command in xaml

I create a VM based on MVVM light toolkit.
In VM, there is a simple ICommand(RelayCommand)
private RelayCommand _myCommand = null;
public RelayCommand MyCommand
{
get
{
if (_myCommand == null) //set break point here for debug
{
_myCommand = new RelayCommand(() =>
{
try
{
//....
}
catch (Exception ex)
{
// notify user if there is any error
//....
}
}
, () => true);
}
return _myCommand;
}
}
then in xaml, just bind this Command property to a button like:
<Button Grid.Column="1" x:Name="Test" Content="Test" Margin="2,0,2,0" Command="{Binding Path=MyCommand}" />
Then run the app, and click on the button, there is no response at all. No error.
VM is working fine. The data has been loaded to a datagrid before I click on the Test button.
If debug the app and put break point, the point is never reached.
How to resolve this problem?
Add a setter to your MyCommand property.
As always, check the Output window for any data binding errors when the XAML is rendered.
Also, try adding a test value converter and putting a breakpoint in the convert method to see if data binding is even being executed on that command. If the breakpoint isn't hit, you know you have a problem in your XAML. If the breakpoint is hit, take a look at the value to see if the data context is correct.
<UserControl.Resources>
<ResourceDictionary>
<TestConverter x:Key="TestConverter" />
</ResourceDictionary>
<Button Grid.Column="1" x:Name="Test" Content="Test" Margin="2,0,2,0" Command="{Binding Path=MyCommand, Converter={StaticResource TestConverter}}" />
</UserControl>
Test value converter - very useful for debugging data binding issues.
public class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("TestConverter.Convert(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
value, targetType, parameter, culture);
return value; // put break point here to test data binding
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("TestConverter.ConvertBack(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
value, targetType, parameter, culture);
return value;
}
}
Works on my machine :)
Seriously, I made a simple project, created a ViewModel, pasted in your code, and it worked. I am guessing you are dealing with some other issue.
Here is my C# code.
Here is my XAML code.
Time to evangelize a bit
This ViewModel code reeks. You might consider using some sort MVVM framework or helpers. If you look at ViewModelSupport, for instance, you can write your ViewModel like this:
public class MyViewModel : ViewModelBase
{
public void Execute_MyCommand()
{
// Your execution code here
}
}
Then, you avoid all that messy plumbing. Just think about it :)
the code looks fine. so you just have to check the output window for databinding errors. maybe you did not set the datacontext of the view correct. btw you should add your breakpoint in the try-catch of the command.
1) Make sure you're returning true from the relay command's CanExecute delegate. (I see you are doing this but good to double check).
2) Is the button inside a ListBox, DataGrid or DataForm?
For a ListBox or DataGrid:
If so you need to modify your binding expression to refer to the VM DataContext as opposed to the databound item. See this answer.
For a DataForm :
More tricky, but look at this question.

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

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. :)

Resources