Missing something on WPF Binding - c#

Just starting WPF & cannot get simple WPF Binding on DataGrid to work and I do not know how to debug. The bound class initializer executes but nothing shows on DataGrid. Minimum code behind and I kept the XAML & bound objects as simple as possible. Thank you for any help.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" enter code heremc:Ignorable="d" x:Class="WpfBinding.MainWindow"
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<local:Simple x:Key="keySimple"/>
</Window.Resources>
<Grid>
<DataGrid x:Name="dg" AutoGenerateColumns="True"
DataContext="{Binding Source={StaticResource keySimple}}"
ItemsSource="{Binding Path=Numbers}">
</DataGrid>
</Grid>
</Window>
namespace WpfBinding
{
public class Simple
{
public List<Number> Numbers = new List<Number>();
public Simple()
{
Numbers.Add(new Number(5));
Numbers.Add(new Number(6));
}
}
public class Number
{
private int nmb;
public Number(int x) { nmb = x; }
}
}

Bindings works only on properties and not member variables.
Just change your class to
public class Simple
{
public List<Number> _numbers = new List<Number>();
public List<Number> Numbers { get { return _numbers; } }
public Simple()
{
_numbers.Add(new Number(5));
_numbers.Add(new Number(6));
}
}
public class Number
{
public int NMB { get; set; }
public Number(int x) { NMB = x; }
}

Related

Populating a DataGrid with a List from a custom Class

I have a class with a property that holds a list of a custom object.
// MySongs.cs
public class MySongs
{
public List<Song> Songs = new List<Song>();
}
The Songs property gets populated in MainWindow().
// MainWindow.xaml.cs
MySongs.Songs.Add(new Song("Hey Jude", "The Beatles"));
How can I display the MySongs.Songs list in a DataGrid with Title and Artist as headers?
Thanks!
Edit (10/27):
Here is the XAML in MainWindow.xaml:
<Window x:Class="MySongsUI.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:local="clr-namespace:MySongsUI"
mc:Ignorable="d"
Title="My Songs" Height="450" Width="800">
<Grid>
<!-- DataGrid with Songs -->
<DataGrid x:Name="songsDataGrid" />
</Grid>
</Window>
Here is the C# in MySongs.cs:
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
namespace MySongsUI
{
public static class MySongs
{
public static ObservableCollection<Song> Songs = new ObservableCollection<Song>();
}
}
Here is the C# in Song.cs:
namespace MySongsUI
{
public class Song
{
public string Title { get; set; }
public string Artist { get; set; }
// CONSTRUCTOR
public Song(string title, string artist)
{
Title = title;
Artist = artist;
}
}
}
I guess I'm not sure of the best way to make the MySongs class be recognized in the XAML of MainWindow.xaml so I can bind it to the DataGrid.
Thank you!
Here is a very simple example code that will auto generate all your columns according to the properties of the Song class:
<DataGrid ItemsSource="{Binding MySongs}" AutoGenerateColumns="True">
</DataGrid >
If you want anything custom, you will need to style it.
Don't forget to set the list to be an ObservableCollection and the Song class to inherit from NotificationObject if you want to make changes to them on runtime.
Hope this helps..
Edit:
Here is how it should look like:
MainWindlow:
<Window x:Class="WpfApplication1.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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<local:MainView />
</Window>
MainView.xaml.cs:
public MainView()
{
DataContext = new MainViewModel();
InitializeComponent();
}
MainView.xaml:
<DataGrid ItemsSource="{Binding local:MySongs.Songs}" AutoGenerateColumns="True">
</DataGrid >
MainViewModel.cs:
public class MainViewModel
{
public MySongs MySongs { get; set; }
public MainViewModel()
{
MySongs = new MySongs()
{
Songs = new ObservableCollection<Song>()
{
new Song("Hey Jude", "The Beatles")
}
};
}
}
You will have to implement INotifyPropertyChanged on MainViewModel, Song and MySongs in order to support runtime change of the data.

How can I restore the state of a ComboBox?

I have an array of objects NamedFoos in my ViewModel that represents the available choices for a combobox control.
The problem is that when I serialize the bound variable and then deserialize it again in a later run of the application, the combobox does not show the value of the object it is bound to.
The combobox is empty upon startup.
The source of the problem appears to be that the newly created item is not recognized as an element of the collection.
I wrote a little test app and can reproduce the problem by simply assigning a value to the underlying field of the bound variable (see code below).
If I change the assignment from
_selectedFoo = new Bar(2);
to
_selectedFoo = NamedFoos[1];
it works as expected.
I'm aware that de-/serializing just the index of the selection would solve the problem, however serializing the entire object is a requirement.
How can I let the combobox display the restored value even if it's not part of the ItemsSource collection?
xaml
<Window x:Class="test_app.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:local="clr-namespace:test_app"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel x:Name="ViewModel"/>
</Window.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding NamedFoos}"
SelectedItem="{Binding SelectedFoo}"
DisplayMemberPath="ComboBoxDisplayName"/>
</StackPanel>
ViewModel
using System.Runtime.Serialization;
namespace test_app
{
public class ViewModel
{
private Foo _selectedFoo;
public ViewModel()
{
_selectedFoo = new Bar(2);
}
public Foo[] NamedFoos { get; } =
{
new Foo(1),
new Bar(2)
};
public Foo SelectedFoo
{
get => _selectedFoo;
set => _selectedFoo = value;
}
[DataContract]
public class Foo
{
[DataMember]
public int Num { get; set; }
public Foo(int num)
{
Num = num;
}
public virtual string ComboBoxDisplayName => "foo";
}
class Bar : Foo
{
public Bar(int num) : base(num)
{
}
public override string ComboBoxDisplayName => "bar";
}
}
}

Window property and Checkbox databinding xaml

I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...

How to bind a DataGrid to another class' list?

I have a DataGrid. I want to bind it to a List, which is in another class. Can I say the following?
<DataGrid ItemsSource="{Binding AnotherClass.Instance.MyList}">
...
</DataGrid>
I think this should work :
<Grid>
<DataGrid x:Name="MyDatagrid" ItemsSource="{Binding Path=MyList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CanUserAddRows="False">
</Grid>
And set the itemsSource programmaticaly :
MyDatagrig.ItemsSource = MyClass.MyList;
I suggest using an MVVM approach.
Use an MVVM framework (like Prism, MvvMLight) or create by yourself a class for all viewmodels registration:
Locator.cs
public class Locator
{
public AnotherClass Another
{
get
{
return AnotherClass.Instance;
}
}
}
Add Locator.cs as an available resources for your view, so you can call your property setting the right DataContext:
MainWindow.xaml
<Window x:Class="DataGridBindingExample.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:local="clr-namespace:DataGridBindingExample"
mc:Ignorable="d"
xmlns:vm="clr-namespace:DataGridBindingExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:Locator x:Key="Locator" />
</Window.Resources>
<DataGrid DataContext="{Binding Another, Source={StaticResource Locator}}" ItemsSource="{Binding MyList}">
</DataGrid>
</Window>
AnotherClass.cs
public class AnotherClass
{
private static AnotherClass instance;
private AnotherClass() { }
public static AnotherClass Instance
{
get
{
if (instance == null)
{
instance = new AnotherClass();
}
return instance;
}
}
public IList<string> MyList { get; set; } = new List<string>
{
"one",
"three"
};
}

Databinding a Datagrid

After being unsuccessful in databinding my Observable collection to my datagrid (Another question in this same forum), i tried to reduce the scope. Now my project has only one datagrid, one ObservableColection and one class.
But still my databinding is failing.. please help..
using System.Collections.ObjectModel;
using System.Windows;
namespace TestDatagrid
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainViewModel
{
public ObservableCollection<OptionStrike> oOs = new ObservableCollection<OptionStrike>(new OptionStrike[]
{
new OptionStrike("Put", 7500.00, 12345),
new OptionStrike("Call", 7500.00, 123),
new OptionStrike("Put", 8000.00, 23645),
new OptionStrike("Call", 8000.00,99999)
});
}
public class OptionStrike
{
public OptionStrike(string p1, double p2, int p3)
{
// TODO: Complete member initialization
this.Type = p1;
this.Strike = p2;
this.Volume = p3;
}
public string Type { get; set; }
public double Strike { get; set; }
public double Volume { get; set; }
}
}
this is my XAML..
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestDatagrid" x:Class="TestDatagrid.MainWindow" Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<DataGrid ItemsSource="{Binding oOs}" AutoGenerateColumns="True" />
</StackPanel>
</Grid>
</Window>
You need to expose your ObservableCollection as a Property, not a Field.
public class MainViewModel
{
public ObservableCollection<OptionStrike> oOs { get; set; }
public MainViewModel()
{
oOs = new ObservableCollection<OptionStrike>(new OptionStrike[]
{
new OptionStrike("Put", 7500.00, 12345),
new OptionStrike("Call", 7500.00, 123),
new OptionStrike("Put", 8000.00, 23645),
new OptionStrike("Call", 8000.00,99999)
});
}
}
You cannot bind to fields, see here for some more information on the subject.

Categories

Resources