views:

357

answers:

3

I've got a simple Item-class, that looks like this:

public class Item : DependencyObject
{
 public int No
 {
  get { return (int)GetValue(NoProperty); }
  set { SetValue(NoProperty, value); }
 }

 public string Name
 {
  get { return (string)GetValue(NameProperty); }
  set { SetValue(NameProperty, value); }
 }

 public static readonly DependencyProperty NoProperty = DependencyProperty.Register("No", typeof(int), typeof(Item));
 public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Item));
}

And a ValueConverter, that looks like this:

[ValueConversion(typeof(Item), typeof(string))]
internal class ItemToStringConverter : IValueConverter
{
 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
 {
  if (value == null)
  {
   return null;
  }

  var item = ((Item)value);

  var sb = new StringBuilder();
  sb.AppendFormat("Item # {0}", item.No);

  if (string.IsNullOrEmpty(item.Name) == false)
  {
   sb.AppendFormat(" - [{0}]", item.Name);
  }

  return sb.ToString();
 }


 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
 {
  throw new NotImplementedException();
 }
}

On my WPF Window, I declare a DependencyProperty (called Items) that holds a list of Item objects (List< Item >) and creates a ComboBox that binds to this DependencyProperty using this XAML-code:

<ComboBox ItemsSource="{Binding ElementName=mainWindow, Path=Items}">
 <ComboBox.ItemTemplate>
  <DataTemplate>
   <TextBlock Text="{Binding Converter={StaticResource itemToStringConverter}}"/>
  </DataTemplate>
 </ComboBox.ItemTemplate>
</ComboBox>

If I execute the code below once, the databinding works fine, howevery the value convertion fails if I execute it again:

var item1 = new Item {Name = "Item A", No = 1};
var item2 = new Item {Name = "Item B", No = 2};
var item3 = new Item {Name = "Item C", No = 3};
Items = new List<Item> {item1, item2, item3};

The problem is, that the ItemToStringConverter.Convert method is now passed a string-object as the first parameter instead of an Item-object?

What am I doing wrong?

Regards, Kenneth

A: 

If you want to use databinding, you should assign your collection to ItemsSource, not Items. I think that would probably fix this problem.

Joe White
I think I've done that haven't I?The ComboBox's ItemsSource property is bound the Items-property...Please correct me if I'm wrong.
kennethkryger
D'oh! Missed that -- I assumed it was the ListBox's Items that you were assigning to, not your object's. My bad.
Joe White
A: 

Alternative approach,

  1. You can derive your class from Object instead of DependencyObject
  2. You can implement INotifyPropertyChange interface
  3. And you can implement a read only property and fire notify event when any of No or Name changes.

public class Item : System.ComponentModel.INotifyPropertyChanged {

public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;


private void Notify(string p)
{
    if (PropertyChanged != null)
        PropertyChanged(this,
            new System.ComponentModel.PropertyChangedEventArgs(p));
}

private int _No = 0;
public int No
{
    get
    {
        return _No;
    }
    set
    {
        _No = value;
        Notify("No");
        Notify("DisplayName");
    }
}


private string _Name = "";
public string Name
{
    get
    {
        return _Name;
    }
    set
    {
        _Name = value;
        Notify("Name");
        Notify("DisplayName");
    }
}

public string DisplayName
{
    get
    {
        string sb = string.Format("Item # {0}", _No);
        if (!string.IsNullOrEmpty(_Name))
            sb += _Name;
        return sb;
    }
}

}

Now you can only bind "DisplayName" property instead of converter..

  1. Converters are pretty complex to implement
  2. And DependencyObject based classes should only be used for UI purposes
Akash Kava
My sample is simplied a great deal from my original code, and the Item class i actually derived from BusinessBase (I'm using CSLA.NET).However, adding a DisplayName property is a great idea.But I still can't figure out, why my converter is called with a string value?
kennethkryger
A: 

Simple workaround would be to check the type passed to your ValueConverter. Change Convert method as follows:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        var item = value as Item;
        if(item == null) { return null; }
        var sb = new StringBuilder();
        sb.AppendFormat("Item # {0}", item.No);
        if(string.IsNullOrEmpty(item.Name) == false) {
            sb.AppendFormat(" - [{0}]", item.Name);
        }
        return sb.ToString();
    }
Stanislav Kniazev
That's exactly what I've done so far, but I really wanted to find out, why the Convert-method was called with a string value :)
kennethkryger