【问题标题】:Binding a control to a data class using WPF/XAML使用 WPF/XAML 将控件绑定到数据类
【发布时间】:2011-04-01 00:03:34
【问题描述】:

我正在尝试将数据类绑定到 WPF 中的 ListView 控件,但似乎无法使其正常工作。我可以在运行时绑定它并使用以下方法使其工作:

this.DataContext = DataSet;

但是,如果我在 WPF/XAML 中尝试以下操作,它将不起作用:

DataContext="DiscoveredItemContainer"

我尝试了各种排列,但我尝试的都没有。我可以只使用运行时版本,因为它可以工作,但我无法让 XAML 正确绑定控件,这让我很烦恼。也许这行不通,因为数据集本质上是动态的,但这只是一个想法。

我不确定代码是否有助于回答这个问题,但我会发布相关的内容,我希望任何方式。我省略了 using 声明。

这是表单的 XAML

<Window x:Class="Viking.Test.DataBindTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Items="clr-namespace:Viking.Test.Discovery"
xmlns:data="clr-namespace:Viking.Test"
Title="Data Binding Test" Height="300" Width="500"
DataContext="DiscoveredItemContainer"> 
  <DockPanel Name="_DockPanel" Height="Auto" Width="Auto">
    <Menu Name="_Menu" DockPanel.Dock="Top" Height="22" Width="Auto" VerticalContentAlignment="Center" VerticalAlignment="Top">
      <MenuItem Name="_File" Header="File">
        <MenuItem Name="_AddOne" Header="Add One" Click="AddOne_Click" />
      </MenuItem>
    </Menu>
    <ListView Name="listView1" Height="Auto" Width="Auto" ItemsSource="{Binding Path=DiscoveredItems}">
      <ListView.View>
        <GridView AllowsColumnReorder="True">
          <GridViewColumn Header="Field1" DisplayMemberBinding="{Binding Field1}" Width="100" />
        </GridView>
      </ListView.View>
    </ListView>
  </DockPanel>
</Window>

这是 XAML 的部分类

namespace Viking.Test 
{
    public partial class DataBindTest : Window
    {
        private DiscoveredItemList DiscoveredItemContainer;

        public DataBindTest() 
        {
            InitializeComponent();
            DiscoveredItemContainer = new DiscoveredItemList();
            // Uncomment the following line to get the databinding to work
            //  this.DataContext = DiscoveredItemContainer;
        }

        private void AddOne_Click(object sender, RoutedEventArgs e) 
        {
            DiscoveredItemContainer.AddRandomItem();
        }
    }  // End of Class 
}  // End of Namespace 

The following is the class that contains the dataset
namespace Viking.Test.Discovery 
{
    public class DiscoveredItem 
    {

        public DiscoveredItem() 
        {
        }   

        public string Field1 { get; set; }

    }  // End of Class 
}  // End of Namespace 

最后,这个类将公开 ObservableCollection 变量以将数据绑定到该类

namespace Viking.Test.Discovery 
{
    class DiscoveredItemList 
    {
        public ObservableCollection<DiscoveredItem> DiscoveredItems { get; set; }
        private Random RandomGen;

        public DiscoveredItemList() 
        {
            DiscoveredItems = new ObservableCollection<DiscoveredItem>();
            RandomGen = new Random();
        }

        public void AddRandomItem() 
        {
            DiscoveredItem di = new DiscoveredItem();;
            di.Field1 = RandomGen.Next(1,10).ToString();
            DiscoveredItems.Add(di);
        }
    }  // End of Class 
}  // End of Namespace 

我已经看到大量关于将控件绑定到窗体上的另一个控件,或在运行时绑定(这是我如何使其工作)或绑定到静态资源的文章。
任何关于为什么我不能用这种方法工作的见解都值得赞赏。

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    通过在 XAML 中设置 DataContext,您决定实例化一个新的 DiscoveredItemList,而不是绑定到代码隐藏中的那个。因此,您的代码隐藏不会直接引用 DiscoveredItemList,但您始终可以按名称搜索它。

    您需要做的第一件事是删除:

    DataContext="DiscoveredItemContainer"
    

    并替换为:

    <Window.Resources>
       <Items:DiscoveredItemList x:Key="Context"></Items:DiscoveredItemList>
    </Window.Resources>
    

    这会实例化您的 xaml 现在可以绑定到的新对象。您的列表视图应该以这种方式绑定到它:

    <ListView Name="listView1" Height="Auto" Width="Auto" DataContext="{StaticResource Context}" ItemsSource="{Binding DiscoveredItems}">
    ...
    </ListView>
    

    您还必须在后面的代码中删除所有对 DiscoveredItemContainer 的引用,当您想要访问 Items:DiscoveredItemList 时,例如添加一个新的随机项目,请执行以下操作:

    private void AddOne_Click(object sender, RoutedEventArgs e)
    {
       var list = this.Resources["Context"] as DiscoveredItemList;
       list.AddRandomItem();
    }
    

    如果您愿意,您或许可以存储对该项目的本地引用。

    这是您复制/粘贴乐趣的完整代码。请注意,我已经对此进行了测试并且“它对我有用”:P

    namespace Viking.Test
    {
        public partial class DataBindTest : Window
        {
            public DataBindTest()
            {
                InitializeComponent();
            }
    
            private void AddOne_Click(object sender, RoutedEventArgs e)
            {
                var list = this.Resources["Context"] as DiscoveredItemList;
                list.AddRandomItem();
            }
        }  // End of Class 
    }  // End of Namespace 
    
    //The following is the class that contains the dataset
    namespace Viking.Test.Discovery
    {
        public class DiscoveredItem
        {
            public DiscoveredItem() { }
    
            public string Field1 { get; set; }
    
        }  // End of Class 
    
         public class DiscoveredItemList
        {
            public ObservableCollection<DiscoveredItem> DiscoveredItems { get; set; }
            private Random RandomGen;
    
            public DiscoveredItemList()
            {
                DiscoveredItems = new ObservableCollection<DiscoveredItem>();
                RandomGen = new Random();
            }
    
    
            public void AddRandomItem()
            {
                DiscoveredItem di = new DiscoveredItem(); ;
                di.Field1 = RandomGen.Next(1, 10).ToString();
                DiscoveredItems.Add(di);
            }
        }  // End of Class 
    }
    

    XAML:

    <Window x:Class="Viking.Test.DataBindTest" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Items="clr-namespace:Viking.Test.Discovery" xmlns:data="clr-namespace:Viking.Test" Title="Data Binding Test" Height="300" Width="500" >
        <Window.Resources>
            <Items:DiscoveredItemList x:Key="Context"></Items:DiscoveredItemList>
        </Window.Resources>
        <DockPanel Name="_DockPanel" Height="Auto" Width="Auto" >
            <Menu Name="_Menu" DockPanel.Dock="Top" Height="22" Width="Auto" VerticalContentAlignment="Center" VerticalAlignment="Top">
                <MenuItem Name="_File" Header="File">
                    <MenuItem Name="_AddOne" Header="Add One" Click="AddOne_Click" />
                </MenuItem>
            </Menu>
            <ListView Name="listView1" Height="Auto" Width="Auto" DataContext="{StaticResource Context}" ItemsSource="{Binding DiscoveredItems}">
                <ListView.View>
                    <GridView AllowsColumnReorder="True">
                        <GridViewColumn Header="Field1"   DisplayMemberBinding="{Binding Field1}" Width="100" />
                    </GridView>
                </ListView.View>
            </ListView>
        </DockPanel>
    </Window>
    

    参考: Silverlight - Setting DataContext in XAML rather than in constructor?

    【讨论】:

    • 太棒了,效果很好。我只需要按照您所说的更改那几行,编译并运行项目,然后砰!干得好!!最后,任何一种方法都比另一种更好吗??
    • 这真的是一个偏好问题。但总的来说,UI 和“业务”逻辑之间的分离越清晰,你的情况就会越好。从这个意义上说,您的视图(XAML 代码)不应该知道也不关心它的数据上下文是什么,只要 DiscoveredItems 存在于某处。所以我建议你撤消我刚刚发布的所有内容,将 DiscoveredItemContainer 设置为公共属性,然后设置 this.DataContext = DiscoveredItemContainer 。从长远来看,这会少一些麻烦。
    • 您还可以从 UI 和 DiscoveredItemsContainer 类之间的中间层中受益。那将是一个 ViewModel。如果你还没有探索过 MVVM 的世界,请一定要花一些时间来观看这个精彩的视频:blog.lab49.com/archives/2650 一旦你掌握了 MVVM,你的 WPF 和绑定技能就会像火箭一样起飞,你会编写比以往更简洁的代码。
    • 谢谢,Bufferz。这是我第一个使用 WPF 的项目(过去一直使用 WinForms)。我将查看博客和 MVVM,看看是否可以将其合并到我的代码中。
    【解决方案2】:

    你应该替换

    DataContext="DiscoveredItemContainer"
    

    通过

    DataContext="{Binding DiscoveredItemContainer}"
    

    【讨论】:

    • 那不行,因为 (a) DiscoveredItemContainer 是私有的,(b) DataContext 不是当前对象
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多