This project has moved. For the latest updates, please go here.

(very) Basic Databinding question

Nov 4, 2015 at 3:09 AM
Hi all! I have a novice question that I can't seem to find quite the right answer to. I'm following the tutorial found here. I can't seem to get the DataGridCollectionViewSource to correctly bind to a property in my app. Here's my XAML, basically a copy and past from the tutorial:
<Grid x:Name="factorGrid" xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid" HorizontalAlignment="Left" Height="406" Margin="346,64,0,0" VerticalAlignment="Top" Width="350" Background="#FFD1CCCC">
            <Grid.Resources>
                <xcdg:DataGridCollectionViewSource x:Key="key_factors" Source="{Binding Source={x:Static Application.Current}, Path=FactorSet }"/>
            </Grid.Resources>
            <xcdg:DataGridControl x:Name="FactorsGrid" ItemsSource="{Binding Source={StaticResource key_factors}}"/>
        </Grid>
Nothing is showing up when I run my app - however, in the VS xaml screen, I can see the squiggly line under Path=FactorSet, and hovering over it says : "Cannot resolve property FactorSet in data context of type 'System.Windows.Application'

And here's part of my class (VS 2015, c#) - I have abbreviated and removed things not relevant to the question. I could be approaching this the wrong way (I'm still learning!), so suggestions are appreciated. (I'm also trying to learn how to implement MVC. So if that looks way, waaay off... well, I'm still learning :) )
public partial class MainWindow : Window, IObserver<ResearchFactor>
    {
        private string _designSelection;
        private IController _controller;
        List<ResearchFactor> _factors;
        private static DataTable factorSet;
        
        public static DataTable FactorSet => factorSet;
public MainWindow()
        {
            //InitializeComponent();
            
        }

        public MainWindow(string designSelection, IModel model, IController controller)
        {
            InitializeComponent();
            _factors = new List<ResearchFactor>();
            _designSelection = designSelection;
            designLabel.Content += designSelection;
            _controller = controller;
            updateFactorList();
        }
//more code
internal void updateFactorList()
        {
            factorSet = CreateDataTable();
            DataGridControl dgControl = new DataGridControl
            {
                ItemsSource = new DataGridCollectionView(FactorSet.DefaultView)
            };
        }
public DataTable CreateDataTable()
        {

            ///this part sets up the structure of the datatable           
            Type entityType = typeof(ResearchFactor);
            DataTable dt = new DataTable(entityType.Name);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

            foreach (PropertyDescriptor prop in properties)
            {
                dt.Columns.Add(prop.Name, prop.PropertyType);
            }

            //now we need to add the data to the table.
            foreach (ResearchFactor factor in _factors)
            {
                DataRow row = dt.NewRow();

                foreach (PropertyDescriptor prop in properties)
                {
                    row[prop.Name] = prop.GetValue(factor);
                }
            }
            
            return dt;
        }
Could it be that my class is not static?

Thanks for the help!
Nov 4, 2015 at 9:23 AM
I would place your collectionviewsource in the UserControl or Window resources section rather than within the grid that you are working against.
Nov 4, 2015 at 12:17 PM
netniv wrote:
I would place your collectionviewsource in the UserControl or Window resources section rather than within the grid that you are working against.
Thanks for the reply! I tried putting it in <Window.Resources> but resulted in the same issue (Cannot resolve property FactorSet in data context of type 'System.Windows.Application'). I'm sure I'm missing something simple. Any ideas?
Nov 4, 2015 at 1:28 PM
Edited Nov 4, 2015 at 1:30 PM
Actually, I just realised why you are getting this error. The reason is because you are attempting to bind statically to the Application.Current which will be App.xaml/.cs not MainWindow.xaml/cs.

You have a choice between
  1. Change the static binding to be for your MainWindow. The simpliest way to do that would be to remove the static of the readonly auto property and then simply using a {Binding FactorSet} after setting the datacontext to the mainwindow. If you are using a different datacontext as the default context, you will have to then use ElementName and reference the window by name.
  2. Reference Application.Current.MainWindow assuming that your MainWindow is the first and main window of the application.
Nov 4, 2015 at 3:23 PM
netniv wrote:
Actually, I just realised why you are getting this error. The reason is because you are attempting to bind statically to the Application.Current which will be App.xaml/.cs not MainWindow.xaml/cs.

You have a choice between
  1. Change the static binding to be for your MainWindow. The simpliest way to do that would be to remove the static of the readonly auto property and then simply using a {Binding FactorSet} after setting the datacontext to the mainwindow. If you are using a different datacontext as the default context, you will have to then use ElementName and reference the window by name.
  2. Reference Application.Current.MainWindow assuming that your MainWindow is the first and main window of the application.
Thanks again! I think I'm getting there, but still having trouble. Looks like I have a lot to learn about XAML.

I tried option 1 - removed static from FactorSet; then created a datacontext that pointed to mainwindow as such:
<Window.DataContext>
        <local:MainWindow />
    </Window.DataContext>
I was then able to actually use the GUI to create the data binding by clicking on DataGridCollectionViewSource, going to the property window, then clicking on the box next to source and clicking "Create Data Binding". Data context was selected and in the path window on the right, I scrolled down and found FactorSet and clicked OK. That resulted in the following:
<xcdg:DataGridCollectionViewSource x:Key="key_factors" Source="{Binding FactorSet, Mode=OneWay}"  />
Wit this, there were no more complaints of not being able to resolve names, but still no dice on displaying any data. On loading, there's nothing in the panel.

I hate to keep asking for help when I need to just read a book on XAML (I am reading a book on C# 5.0 and the .NET 4.5 framework, but I haven't gotten to any XAML yet), so thanks for your patience!
Nov 5, 2015 at 8:54 AM
Edited Nov 5, 2015 at 9:45 AM
You shouldn't really create a second copy of the window that you already have. You should instead reference the window by name.
<Window x:Name="MyWindow">
    <Grid DataContext="{Binding ElementName=MyWindow}">
...
    </Grid>
</Window>
You can also enable tracesources that allow you to see what WPF is doing with the binding, see: Trace sources in WPF and How to debug WPF bindings (You will want the Trace Level – new in 3.5 section)

Note 1: I have written this by hand so it might not be 100% accurate.

Note 2: You should also read up on WPF because it sounds like this is your first real dive into it. At first, it's going to seem a LOT different to WinForms or ASP.NET if you've done those. But, in time, it does become easier and I actually prefer to use it these days.

These sites should help you:
Welcome to the WPF Tutorial
The complete WPF tutorial
WPF Tutorial : Beginning
Walkthrough: Getting Started with WPF
Nov 6, 2015 at 3:13 AM
Sooooooo.... first of all, I would like to sincerely express my gratitude for taking the time to help me so much with this! You are correct - I am a COMPLETE novice at WPF. Those are great resources and I will definitely read through them!

And I finally solved my problem - it was far more insidious than data binding. After beating my head on it for WAY too long and thinking that it could NOT POSSIBLY be this difficult to tell one thing to point to another, I finally decided that I was probably doing something else wrong. And it turns out that I was.

Before I decided to use the WPF toolkit's datagrid, I had a grid that I was RE-drawing each time the user went and edited the factors in the edit factors window. I removed MOST of that code, except one little piece that I didn't realize was screwing me up. And that piece was:
factorGrid.Children.Clear();
I didn't even realize it was still there, and this was obliterating my datagrid before it could even do anything. sigh So thank you for the help! Looks like I need to remember to think outside the box. Or something.
Nov 9, 2015 at 3:09 AM
I have another question. After this last post, I changed my design around a lot and got the datagrid to show up initially with some fake data that I put in when I initialized it. However, whenever I would add or remove rows to my datatable, the datagrid would not get the update and would only ever display the initial test row that was placed in it.

I FINALLY fixed it by changing my dataTable back to static. Now I can add and delete data as I intend to, but I'm not sure I understand why this fixed it. Any idea why? Here's the updated code that works.. I am attempting to use an MVC type model, so my DataTable is now coming from a "model" class.

Thanks!
class Model : IModel
    {
        private static DataTable factorSet; // <--- why did making this static fix the problem?
        private DataGridControl dgControl;
        
        public DataTable FactorSet => factorSet;

        public Model()
        {
            
            Type entityType = typeof(ResearchFactor);
            factorSet = new DataTable(entityType.Name);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

            foreach (PropertyDescriptor prop in properties)
            {
                factorSet.Columns.Add(prop.Name, prop.PropertyType);
            }
            //set primary key. Not sure how to use this, but hopefully I can figure this out.
            DataColumn[] column = new DataColumn[1];
            column[0] = factorSet.Columns["Label"];
            factorSet.PrimaryKey = column;

            DataRow rowTest = factorSet.NewRow(); 
            rowTest["Label"] = "testRow";
            rowTest["isRandomized"] = true;
            rowTest["isWithinSubjects"] = false;
            rowTest["Levels"] = 5;

            factorSet.Rows.Add(rowTest); //until I made the datatable, static, this was the only thing that would ever show up!

            dgControl = new DataGridControl
            {
                ItemsSource = new DataGridCollectionView(FactorSet.DefaultView)
            };
         //code for adding/removing factors
        }
<Window x:Name="M_Window" x:Class="RCCDTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
        xmlns:local="clr-namespace:RCCDTool"
        mc:Ignorable="d"
        Title="MainWindow" Height="555" Width="831">
    <Window.Resources>
        <local:Model x:Key="Model"></local:Model>
    </Window.Resources>
<!-- more code here -->
<Grid x:Name="factorGrid" xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid" HorizontalAlignment="Left" Height="406" Margin="263,64,0,0" VerticalAlignment="Top" Width="541" Background="#FFD1CCCC">

            <Grid.Resources>
                <xcdg:DataGridCollectionViewSource x:Key="ListOfFactors" Source="{Binding FactorSet, Mode=OneWay, Source={StaticResource Model}}"/>

            </Grid.Resources>
            <xcdg:DataGridControl x:Name="FactorsGrid" ItemsSource="{Binding Source={StaticResource ListOfFactors}}" />
        </Grid>
Nov 9, 2015 at 8:54 AM
I will refer you back to my second reply above. It's because you are creating multiple instances rather than sharing instances.