绑定到通用属性

3

我想将一个ListBox绑定到ViewModel中的一个属性。我希望能够随时将该属性替换为另一个具有相同字段的列表。目前,我的代码如下:

public List<City> Cities
{
    get { return GetCities(); }
}

当我使用通常的方式进行绑定时,如下所示:

ItemsSource="{Binding Cities}"

它的运行良好。我还有另一个封闭类名为Rivers,具有相同的公共属性(名称)。

public List<River> Rivers
{
    get { return GetRivers(); }
}

他们都有一个共同的属性“Name”,这是Listbox显示的内容。在Rivers,Cities和其他类之间进行交换的最佳方法是什么?从理论上讲,我希望有一个通用的List属性可以绑定并将其值设置为Rivers或Cities等,但我认为不可能创建通用的List属性。所有类都是密封的,我不能修改它们。
有任何建议吗? 谢谢

什么决定了使用哪个列表? - d.moncada
2个回答

4
如果您无法更改原始类,则最好为River和City创建两个适配器类,每个类都实现具有Name属性的公共接口。
这样,您可以只使用单个列表来表示它们中的每一个,并随意在两者之间切换。
例如。
public interface INamed
{
   string Name { get; }
}

public class RiverAdapter : INamed
{
  private River _river;
  public string Name { get { return _river.Name; } }
}
public class CityAdapter : INamed
{
  private River _city;
  public string Name { get { return _city.Name; } }
}

所以你的列表声明将变为:
public IEnumerable<INamed> Items { get; set; }

1
+1 我错过了 OP 的 sealed/不可修改方面。适配器模式是在这种情况下很好的选择。 - Paul Sasik
谢谢。我尝试过使用接口,但无法使它们正常工作,但这个适配器模式是我需要的。 - NewCode

1
你可以使用绑定到ListBox的动态列表,根据需要显示内容来填充它,并且你可以使用DisplayMemberPath在ListBox中显示Name或任何其他常见属性。
工作示例:
代码:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private ObservableCollection<dynamic> _listBoxItems = new ObservableCollection<dynamic>();
    public ObservableCollection<dynamic> ListBoxItems
    {
        get { return _listBoxItems; }
        set { _listBoxItems = value; }
    }


    public List<River> Rivers
    {
        get
        { 
            return new List<River>
            { 
               new River { Name = "River1" } ,
               new River { Name = "River2"}
            };  
        }
    }

    public List<City> Cities
    {
        get
        {
            return new List<City>
            { 
               new City { Name = "City1" } ,
               new City { Name = "City2"}
            };  
        }
    }

    private void button_City_Click(object sender, RoutedEventArgs e)
    {
        ListBoxItems.Clear();
        Cities.ForEach(i => ListBoxItems.Add(i));
    }

    private void button_River_Click(object sender, RoutedEventArgs e)
    {
        ListBoxItems.Clear();
        Rivers.ForEach(i => ListBoxItems.Add(i));
    }
}

public sealed class City
{
    public string Name { get; set; }
}

public sealed class River
{
    public string Name { get; set; }
}

Xaml:

<Window x:Class="WpfApplication10.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="199" Width="191" Name="UI">
    <Grid DataContext="{Binding ElementName=UI}" >
        <ListBox ItemsSource="{Binding ListBoxItems}" DisplayMemberPath="Name" Margin="0,0,0,29" />
        <Button Content="City" Height="23" HorizontalAlignment="Left" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button_City_Click"/>
        <Button Content="River" Height="23" HorizontalAlignment="Right" Name="button2" VerticalAlignment="Bottom" Width="75" Click="button_River_Click"/>
    </Grid>
</Window>

结果:

enter image description here


我得到了“无法将类型Generic.List<Rivers>转换为Generic.List<dynamic>”的错误。 - NewCode
1
@NewCode:你不能将其转换为List:如果可以,你将能够在河流列表中插入城市。那是错误的。尝试IEnumerable<dynamic>。 - user180326
使用 dynamic 是相当不必要的。将属性作为非泛型的 IListICollection,或者作为 IEnumerable<object>,都可以很好地工作,而使用 dynamic 可能会导致性能下降。Binding 不关心它是 object 还是 dynamic,它仍然会反射它。 - Duncan Matheson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接