Databinding with kind of join over two lists - wpf

I am familiar with the basics of databinding in wpf. However I now have a problem which I wonder how to solve.
Imagine following use case:
I have a global ObservableCollection called "AItems" of Type A.
I have some Objects of Type B and each has a ObservableCollection "BItems" of type A.
The BItems Collections can contain Objects of the global AItems Collection.
I want to visualize this by a ListView.
Each line should contain an A-Object and a checkbox.
I want the ListView to show all elements of the AItems-Collection. Items which are assigned to the B-Object should be marked with a checked checkbox. All other checkboxes should be unchecked.
My questions are now:
How should I set the datacontext?
How can I make that checking a checkbox inserts its item to the BItems-Collection and unchecking removes it?
I hope anyone can understand my problem.
Thanks for replies.

I'm not clear on the latter part of your question. Partly it's because your naming convention is confusing; I'd expect a collection named BItems to contain objects of type B, not A.
So I'm going to change your nomenclature a bit so that I don't get confused. Instead of A, I'll call the first class User, and instead of B, I'll call the second class Group. A Group contains a collection of User objects, named Users. The global collections look like this:
List<User> Users;
List<Group> Groups;
It's easy to determine if a given User u is in any group:
return Groups.Where(g => g.Users.Contains(u)).Any();
Easy, but computationally expensive if you have many groups and they contain many users. We'll get back to that in a second.
Right away, I see that one of your questions has got a problem:
How can I make that checking a checkbox inserts its item to the BItems-Collection and unchecking removes it?
What should happen if I check an unchecked user? Which group (or groups, since more than one group can contain a user) should it be added to?
Since you say that you want checked items to be "assigned to the B-Object", I'm going to assume that the UI is only looking at one group at a time - we'll call it the SelectedGroup. This is good, because g.Users.Contains(u) is much less expensive than the query I showed above.
If this is so, what you need to do is wrap your User in a class that exposes an IsChecked property. I'd call this class UserViewModel, since that's what it is. The class needs three properties (at a minimum):
public User User { get; set; }
public Group SelectedGroup { get; set; }
public bool IsChecked
{
get { return SelectedGroup.Users.Contains(this.User); }
set
{
if (value != IsChecked)
{
if (IsChecked)
{
SelectedGroup.Users.Remove(this.User);
}
else
{
SelectedGroup.Users.Add(this.User);
}
}
}
}
Your ListView is bound to an ObservableCollection<UserViewModel> named, say, UserViewModels. Whenever SelectedGroup is set, you need to rebuild this collection:
UserViewModels = new ObservableCollection<UserViewModel>(
Users.Select(u => new UserViewModel { User=u, SelectedGroup=SelectedGroup }));
You could avoid rebuilding the collection by implementing INotifyPropertyChanged in the UserViewModel class, and having it raise PropertyChanged for the IsChecked property whenever SelectedGroup changes.
Also, it would probably be responsible to include null-reference checking in the IsChecked property, so that the program doesn't throw an exception if SelectedGroup or SelectedGroup.Users is null.

You can bind a list box to Aitems and using a converter to set the isChecked property
<ListBox ItemsSource="{Binding AItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" IsChecked="{Binding Mode=OneTime, Converter={StaticResource BItemCheckConverter}}"></CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class BItemCheckConverter : IValueConverter
{
public List<Aitems> BItems { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (BItems.Contains((value as Aitems)) return true;
else return false
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I don't know how's your object model, so take the above code as is.
HTH

Related

WPF binding tricky issue

I need some assistance to implement some data binding. My viewmodel exposes the following properties:
public List<string> ChosenFeatures {get;set;}
public Dictionary<string, double> AllFeatureCosts {get;set;}
"ChosenFeatures" will contain a subset of dictionary keys present in "AllFeatureCosts".
In the view I would like to render a series of TextBlocks, one for each item in "ChosenFeatures". Here's the tricky part:- the Text property of each TextBlock need to be bound to a value in the "AllFeatureCosts" dictionary, using the string in "ChosenFeatures" as the key to that dictionary item.
I would be grateful for any pointers on how to write the XAML to accomplish this.
Provide a ViewModel for the data, thats the reason to use MVVM in the first place.
class FeatureViewModel
{
public FeatureViewModel(MyViewModel aViewModel, string aKey)
{
mParent = aViewModel;
mKey = aKey
}
public string Value
{
get{return mParent.AllFeatureCosts[mKey];}
}
}
add a collection for your viewmodels to your main viewmodel
public ObservableCollection<FeatureViewModel> Features{ get; set; }
and initialize it somewhere
foreach(var feature in ChosenFeatures)
{
Features.Add(new VisualFeature(this, feature) );
}
from here you can also if necessary and if you have INotifyPropertyChanged properly implemented, raise any changes on the FeatureViewModels. You of course need to keep these collections in sync, which might be a bit of work.
Of course, your DataTemplate needs some adjustments aswell
<DataTemplate DataType="{x:Type FeatureViewModel}">
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
First of all, I suppose you should use #Jay's approach and make ChosenFeatures Dictionary also.
However you can use Converter instead and pass your dictionary like a parameter while binding:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var key = (string)value;
var dictionary = (Dictionary<string, double>)parameter;
if (dictionary.ContainsKey(key))
return dictionary[key];
else
return null;
}
Instead of binding "ChosenFeatures" bind "AllFeatureCosts". We know that it will display complete list and we can then write a simple Multibinding visibility converter to display the items which are selected (in ChosenFeatures).
Note:
Depending on the size of the dictionary it may affect application performance...

How to resolve/control property name collision in XAML binding?

Interview Question
Phrased as:
If you have a property name collision, how would you specify the exact property to bind to in a Binding path expression (in XAML)?
I never faced this (property name collision) problem in any binding so far. With some reading I realized that this is possible in case I am binding to a overridden property because then I have two instances of this property (virtual in base, and overriden in derived) as far as resolution using Reflection is concerned. Which is what used by XAML.
Could there be any other case where XAML might face a property name collision?
Is there some support in API to handle/control that? (Instead of of course avoiding a collision)
Thanks for your interest.
Sounds like a complete nonsense to me. Unless they wanted to talk about bindings, using 'disjointed' sources like PriorityBinding and MultiBinding.
Frankly speaking I don't think overwritten properties can be involved into the matter as this is so much out of scope, you could equaly point out explicit interface implementations and many other things, which are clearly outside of WPF domain.
The best way I can think would be to use a ValueConverter. I don't think this really answers the question though since they're asking in a binding path expression, which I haven't seen to be possible. I'm not particularly fond of doing it this way because it feels like a hack, but it works at least for one way binding. Here's an example of how you might do it:
XAML:
<StackPanel Name="stack">
<StackPanel.Resources>
<loc:OverriddenMyPropertyConverter x:Key="BaseMyProperty"/>
</StackPanel.Resources>
<TextBox Text="{Binding Path=MyProperty}"/>
<TextBox Text="{Binding Mode=OneWay, Converter={StaticResource BaseMyProperty}}"/>
</StackPanel>
The DataContext of the StackPanel is an instance of MyClass. The first TextBox is bound to the MyClass.MyProperty property, and the second TextBox will be bound to the MyBaseClass.MyProperty property. Two way binding would be a bit more complex since the object actually being bound to the second TextBox is the MyClass object and not the MyProperty object.
Code:
class MyClass : MyBaseClass
{
string myProperty = "overridden";
public new string MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
class MyBaseClass
{
string baseProperty = "base";
public string MyProperty
{
get { return baseProperty; }
set { baseProperty = value; }
}
}
class OverriddenMyPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as MyBaseClass).MyProperty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

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.

Pass view to viewmodel with datatemplate

I have a window named ParameterEditorView with a ParameterEditorViewModel as DataContext. In the ParameterEditorViewModel I have a list of ParameterViewModel. In the ParameterEditorView I have an ItemsControl whose ItemsSource is binded to the list of ParameterViewModel in the ParameterEditorViewModel. I need the ParameterViewModel to have a reference to the ParameterView (more on that later). In the Resources section of the ParameterEditorView I add the DataTemplate:
<DataTemplate DataType="{x:Type my:ParameterViewModel}" >
<my:ParameterView HorizontalAlignment="Left"/>
</DataTemplate>
So, how can I pass a reference of the ParameterView that is created to show the ParameterViewModel to it?
The reason I need the ParameterView in the ParameterViewModel is the following:
I have a TextBox whose Text property is binded to the PropertyModelView.Name property. But I want to display a default string when the Name is empty or Null. I've tried to set the property value to the default string I want when that happens but the TextBox.Text is not set in this scenario. I do something like this:
private string _name;
public string Name
{
get { return _name; }
set
{
if (value == null || value.Length == 0)
Name = _defaultName;
else
_name = value;
}
}
I've also tried to specifically set the TextBox.Text binding mode to TwoWay without success.
I think this is a defense mechanism to prevent an infinite loop from happening but I don't know for sure.
Any help on this front would also be highly appreciated.
Thanks,
José Tavares
{Binding } has a FallbackValue, btw.
Your question, it confuses me. I'd assume your PVM has a collection of PV's as a public property, which is bound within the UI. Also, I think you're mixing terms. Its Model-View-ViewModel where the ViewModel is the DataContext of the View, and the Model is exposed by the ViewModel via a public property. Sounds like if you're binding the window to a collection of ViewModels they are actually Models. It may seem pedantic, but getting your terms correct will help you research and ask questions.
Another solution would be to add a Converter to your Binding in combination with FallbackValue (I've had to do this, IIRC). That converter would be an IValueConverter that returns "DependencyProperty.UnsetValue" if the string is null or empty. I think this works sometimes because the TextBox will set the bound property to the empty string rather than null if the TB is empty. Here's a little sample to whet your whistle (not guaranteed to work; you need to debug this and tweak it):
public class ThisMightWorkConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
var temp = value as string;
if(string.IsNullOrWhiteSpace(temp))
return System.Windows.DependencyProperty.UnsetValue;
return temp;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return value; // you might need to change this
}
}

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