简体   繁体   中英

Binding a control to a data class using WPF/XAML

I am trying to bind a data class to a ListView control in WPF, but can't seem to get it working. I can bind it at run-time and get it working using the following:

this.DataContext = DataSet;

But, if I try the following in the WPF/XAML it doesn't work:

DataContext="DiscoveredItemContainer"

I have tried various permutations, but nothing I try works. I could just use the run-time version, as it works, but it is bugging me that I can't make the XAML bind the control correctly. Perhaps this can't work since the dataset is dynamic in nature, but that is just a thought.

I am not sure if the code will help answer the question or not, but I will post what is relevant, I hope any how. I left out the using declarations.

This is the XAML for the form

<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>

Here is the partial class that goes with the 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 

Finally, this is the class that will expose the variable that is ObservableCollection to bind the data to the class

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 

I have seen a great number of articles of binding a control to another control that is on the form, or binding during run-time (which is how I can get this to work) or binds to static resources.
Any insight as to why I can't get this approach to work is appreciated.

By setting your DataContext in XAML, you've decided to instantiate a new DiscoveredItemList instead of binding to the one in your codebehind. Therefore your codebehind won't have a direct reference to the DiscoveredItemList, but you can always search for it by name.

The first thing you need to do is remove:

DataContext="DiscoveredItemContainer"

and replace with:

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

This instantiates a new object that your xaml can now bind to. Your list view should bind to it this way:

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

You must also gut out all the references to DiscoveredItemContainer in your code behind, and when you want to access the Items:DiscoveredItemList, such as to add a new random item, do this:

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

You could perhaps store a local reference to the item if you want to.

Here's the complete code for your copy/pasting pleasure. Note that I've tested this and "it works for me" :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>

Reference: Silverlight - Setting DataContext in XAML rather than in constructor?

you should replace

DataContext="DiscoveredItemContainer"

by

DataContext="{Binding DiscoveredItemContainer}"

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