简体   繁体   中英

C# WPF - ComboBox DataBinding

I am trying to understand the concept of DataBinding a combobox with an object.

I have the following class:

public class employmentApplication
{
  private byte appType = 0; // 1 = normal; 2 = expedited

  public byte AppType
  {
    get { return appType ; }
    set
    {
      appType = value;
      this.OnPropertyChanged("AppType");
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  void OnPropertyChanged(string propName)
  {
    if (this.PropertyChanged != null)
      this.PropertyChanged(
      this, new PropertyChangedEventArgs(propName));
  }
}

My xaml for the combobox is

<ComboBox>
  <ComboBoxItem Content="Normal" />
  <ComboBoxItem Content="Expedited" />
</ComboBox>

I am not sure where to begin to bind my combobox to the AppType since it has to convert it from a string ("Normal", "Expedited") to a byte (0, 1) and back between the object and the combobox.

Thanks for any help in advance!

You could do this several ways, however here is a simple solution todo what you have asked. Note, your DataContext will need to be set to your instance of the class you want to represent.

<ComboBox SelectedValue="{Binding AppType, Mode=TwoWay}"
          SelectedValuePath="Tag">
    <ComboBoxItem Content="Normal" Tag="0"/>
    <ComboBoxItem Content="Expedited" Tag="1"/>
</ComboBox>

SelectedValue is binded to your property, and SelectedValuePath is the property of ComboBoxItem (in this case) that will represent the selected value.

This is a very simple solution, and you may need to alter it for your needs. Additionally, I would consider using an Enum to represent this data. It will make it a little easier to understand what you are trying todo in code.

Edit


Here is an example of using an enum with the above example. I'm going to use your already existing code as a base.

Create an enum (preferably in a new file).

public enum AppType
{
    Normal,
    Expedited
}

Now modify your property to use the enum

public AppType AppType
{
    get
    {
        return appType;
    }
    set
    {
        if( Equals( appType, value ) ) return;
        appType = value;

        OnPropertyChanged( "AppType" );
    }
}

Now you can use my above example, but with an enum:

<ComboBox SelectedValue="{Binding AppType, Mode=TwoWay}"
          SelectedValuePath="Tag">
    <ComboBoxItem Content="Normal" Tag="{x:Static local:AppType.Normal}"/>
    <ComboBoxItem Content="Expedited" Tag="{x:Static local:AppType.Expedited"/>
</ComboBox>

The following is nice for having a little more control over what is displayed in the ComboBox . However you can also have it display every enum value, which is nice because if you add enum values in the future, they will automatically show up in the ComboBox . See this question and answer for a way of doing that.

My advice is to use ValueConverter. It's more flexable solution than using Tag values and it's looks nice because you are separating conversion logic in its own class. Also I noticed that in your data class you declared PropertyChanged event but not implemented INotifyPropertyChanged interface so you can't have two way binding in this case. Full example:

Your data class (with my fixes)

public class EmploymentApplication : INotifyPropertyChanged
{
    private byte appType = 0; // 1 = normal; 2 = expedited

    public byte AppType
    {
        get { return appType; }
        set
        {
            appType = value;
            OnPropertyChanged("AppType");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    } 
}

Value converter

public class AppTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var b = (byte)value;
        if (b == 1) return "Normal";
        if (b == 2) return "Expedited";
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var strValue = (string) value;
        byte result = 0;
        if (strValue.Equals("Normal", StringComparison.Ordinal))
        {
            result = 1;
        }
        else if (strValue.Equals("Expedited", StringComparison.OrdinalIgnoreCase))
        {
            result = 2;
        }
        return result;
    }
}

xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new EmploymentApplication();
    }
}

Xaml

<Window x:Class="WpfConvertion.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfConvertion"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:AppTypeConverter x:Key="Converter"></local:AppTypeConverter>
</Window.Resources>
<Grid>
    <ComboBox Height="20" SelectedValue="{Binding AppType, Converter={StaticResource Converter}}" SelectedValuePath="Content">
        <ComboBoxItem>Normal</ComboBoxItem>
        <ComboBoxItem>Expedited</ComboBoxItem>
    </ComboBox>
</Grid>

Pay attention on this line: xmlns:local="clr-namespace:WpfConvertion" . You must set your own namespace here instead of my WpfConvertion.

There are several ways to achieve this the simplest could be to add a bind to your AppType with SelectedIndex property of your ComboBox Note that you should add INotifyPropertyChanged to let your binding work

and in code behind do the following

namespace WpfApplication8
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        employmentApplication  emp = new employmentApplication();  

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = emp;  
        }
    }
    public class employmentApplication:INotifyPropertyChanged
    {
        private byte appType = 0; // 1 = normal; 2 = expedited

        public byte AppType
        {
            get { return appType; }
            set
            {
                appType = value;
                this.OnPropertyChanged("AppType");
            }
        }



        public event PropertyChangedEventHandler PropertyChanged;          

        void OnPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(
                this, new PropertyChangedEventArgs(propName));
        }
    }
}

最简单的方法是,如果您的Normal值为0,而Expedited值为1,则使用SelectedIndex进行绑定。

<ComboBox SelectedIndex="{Binding AppType}" >

You could use a converter, or tags, as the other answers have shown. But you could also define your item-source as a class rather than rely on hand-waving to link integers to strings. So your item source items could be a class like this:

public class AppType
{
    public string Name;
    public byte Type;
}

You can then use SelectedValue and SelectedValuePath binding on your combobox to define what changes in your datacontext, and what property is used in the lookup list.

<ComboBox 
    ItemSource = {Binding ListOfAppTypes}
    SelectedValue="{Binding Type, Mode=TwoWay}"
    SelectedValuePath="Name">
</ComboBox>

You could also define a Template for your Combobox where you gain more control over how the lookup list of items is displayed.

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