简体   繁体   中英

How to override ToString() and bind it to WPF

I try to bind mycollection into wpf:

    private ObservableCollection<MyClass> _coll;

    public ObservableCollection<MyClass> Coll
    {
        get
        {
            if (_coll == null)
                _coll = new ObservableCollection<MyClass>();                
            return _coll;
        }

        set
        {       
            _coll = value;
        }           
    }

MyClass :

 class MyClass 
  {
     int Id;
     String Name1;
     String Name2;
  }

And at WPF:

 <ListBox Grid.Row="1" x:Name="lbKey" BorderBrush="Gray"
                                     ItemsSource="{Binding  Path=Coll}"
                                     ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                                     HorizontalContentAlignment="Stretch"
                                     ItemContainerStyle="{StaticResource ResourceKey=lbStyle}">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Border BorderThickness="1" BorderBrush="LightGray" Background="WhiteSmoke"
                                                HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                            <TextBlock Text="{Binding}" TextWrapping="Wrap" Margin="2"
                                                       Background="Transparent"
                                                       HorizontalAlignment="Stretch"/>
                                        </Border>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>

But, when i run program see something like Id.ToString(); So, i have to override ToString() method ,but i can not change MyClass methods.

And i create extension method:

namespace Some.Name.Space
{
   public static class MyClassExtensions
     {
       public static string ToString(this MyClass my)
        {
          return String.Format("{0} {1}"my.Name1,my.Name2);
        }
      }
}

But this is not help to me: i see at my grid strings like this: 1,2 and so on.

Can you tell me, how to override ToString methods at extended method and bind it into WPF.

Extension method don't override ToString() method because details here: How to create extension method for toString?

BUT..

You don't have to override ToString() method. You are binding but you do not point which property may by bind.

Change this:

<TextBlock Text="{Binding}" TextWrapping="Wrap" Margin="2"
                                                       Background="Transparent"
                                                       HorizontalAlignment="Stretch"/>

To this

<TextBlock Text="{Binding Name1}" TextWrapping="Wrap" Margin="2"
                                                       Background="Transparent"
                                                       HorizontalAlignment="Stretch"/>

Then You can bind exactly value of Name1 property.

and You must also change MyClass (you have to have public property nor fields)

      public class MyClass 
      {
          int Id;
          private string _Name1;
          private string _Name2;

          public string Name1
          {
           get { return _Name1; }
           set { _Name1 = value; }
          }

          public string Name2
          {
           get { return _Name2; }
           set { _Name2 = value; }
          }
       }

BUT...

If You want bind like this Text="{Binding} and You want Your own implementation of display MyClass - You have to override ToString method in MyClass like this:

      class MyClass 
      {
         int Id;
         String Name1;
         String Name2;

         public override string ToString()
         {
           return String.Format("{0} {1}", this.Name1, this.Name2);
         }
     }

It is good patern to do this.

BUT if You can not change MyClass - You can create Your own type and pack MyClass with in

          public class NewMyClass 
          {
             public MyClass myclass {get;set;}

             public override string ToString()
             {
               return String.Format("{0} {1}", mycalss.name1, mycalss.name2);
             }
         }

and change

public ObservableCollection<NewMyClass> Coll

in this solution Your xaml is correct

BUT

If MyClass have private fields like You show where is no way to get that fields to dispaly.

I think the better option for this scenario is to create property that gives you a combined name. You can define that property as like this:

 public string DisplayName
  {
     get { return _Name1 + _Name2; }
  }

So that the binding code will be similar to this:

<TextBlock Text="{Binding DisplayName}" TextWrapping="Wrap" Margin="2" ../>

Or the whole class definition will be like the following:

    class MyClass
    {
        public int Id { get; set; }
        private string _Name1;
        private string _Name2;

        public string Name2
        {
            get { return _Name2; }
            set { _Name2 = value; }
        }
        public string Name1
        {
            get { return _Name1; }
            set { _Name1 = value; }
        }
        public string DisplayName
        {
            get { return _Name1 + _Name2; }
        }
    }

This is not the answer to the exact question but This will solve the issue in a pretty good manner.

First create a ValueConverter that will take the MyClass instance as a parameter and dig the private fields from the type and then get the values of those fields to return the correct string.

public class MyClassToStringValueConverter : IValueConverter
{
    private static FieldInfo Name1Field;
    private static FieldInfo Name2Field;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (Name1Field == null)
        {
            Name1Field = typeof(MyClass).GetField("Name1", BindingFlags.NonPublic | BindingFlags.Instance);
        }

        if (Name2Field == null)
        {
            Name2Field = typeof(MyClass).GetField("Name2", BindingFlags.NonPublic | BindingFlags.Instance);
        }

        return string.Format("{0} {1}", Name1Field.GetValue(value), Name2Field.GetValue(value));
    }

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

In your xaml, create a resource of this ValueConverter and use it in the binding, below is an example that binds to MyClassProperty of type MyClass in the viewmodel.

<Window.Resources>
    <local:MyClassToStringValueConverter x:Key="MyClassToStringValueConverter"/>
</Window.Resources>

<TextBox Text="{Binding MyClassProperty, Converter={StaticResource MyClassToStringValueConverter}}"/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM