How to get name of a List<string> from List<List<string>> to bind it dynamically? [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a dynamic DataGrid in Which 1 column contains ComboBox template. Now I'll get 'N' number of comboboxes. Each ComboBox should have different ItemsSource. how can it be achieved?
My dynamic datagrid has the property ItemsSourceBinding. Now I need to provide a DataContext.BindingName to this property at runtime. How can it be achieved?
column.ItemsSourceBinding = new Binding()
{
Path = new System.Windows.PropertyPath("DataContext." + bindStreamList1),
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1)
};
In place of bindStreamList1 I need a name of List<string>. it may be from List<List<string>> or from Dictionary<string,List<string>>

I would recommend you to get familiar with MVVM pattern. There are tons of tutorials on the web.If you want to have two way binding you should implement the INotifyPropertyChanged interface. You can find yourself also very good tutorials on that. I also would recommend to do your bindings in XAML and not in code behind when ever possible.
Here is an example of what I guess you want:
XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid>
<DataGrid ItemsSource="{Binding }"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=AvailableNames}"
SelectedItem="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Path=Name, Mode=OneWay}"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code behind:
public MainWindow()
{
InitializeComponent();
List<MyViewModel1> Items = new List<MyViewModel1>();
Items.Add(new MyViewModel1() { Name = "Leonardo" , AvailableNames = new List<string>() { "Leonardo", "Michael Angelo" } });
Items.Add(new MyViewModel1() { Name = "Michael Angelo", AvailableNames = new List<string>() { "Michael Angelo"} }); // can't make a leonardo out of this item
Items.Add(new MyViewModel1() { Name = "Master Splinter", AvailableNames = new List<string>() { "Master Splinter", "Jon Skeet" } }); // master stays master PERIOD ..... or become Jon Skeet
DataContext = Items;
}
And the MyViewModel1
public class MyViewModel1 : INotifyPropertyChanged
{
private List<string> _availableNames;
private string _name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
public List<string> AvailableNames
{
get
{
return _availableNames;
}
set
{
_availableNames = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Related

Setting DataContext in XAML C# WPF [duplicate]

This question already has answers here:
Why does WPF support binding to properties of an object, but not fields?
(2 answers)
Closed 2 years ago.
So I think the title is a little confusing, but I couldn't think of anything better. Here's what I have:
CueSheet.cs
class CueSheet : INotifyPropertyChanged
{
private ObservableCollection<Track> _tracks;
public ObservableCollection<Track> Tracks
{
get => _tracks;
set
{
if (_tracks != value)
_tracks = value;
NotifyPropertyChanged();
}
}
private string _path;
public string Path
{
get => _path;
private set
{
if (_path != value)
_path = value;
NotifyPropertyChanged();
}
}
... other code...
}
public class Track : INotifyPropertyChanged
{
... INotifyPropertyChanged event handler ...
private string _title;
public string Title
{
get => _title;
set
{
_title = value;
NotifyPropertyChanged();
}
}
... 2 more properties (Frame and Index) coded as above ...
}
MainWindow.xaml.cs
public partial class MainWindow : INotifyPropertyChanged
{
CueSheet cueSheet;
public MainWindow()
{
InitializeComponent();
cueSheet = new CueSheet();
this.DataContext = this;
// DataContext Bindings
txtCueFilePath.DataContext = cueSheet;
dgCueTracks.DataContext = cueSheet;
}
...Other Code...
}
cueSheet is populated later in the code by reading from a file.
MainWindow.xaml
... other code ...
<TextBox Name="txtCueFilePath"
Grid.Row="1" Grid.Column="1"
IsReadOnly="False"
Background="LightGray"
Text="{Binding Path=Path, Mode=OneWay}"/>
... other code ...
<DataGrid Grid.Column="2" Name="dgCueTracks2"
BorderBrush="Black" BorderThickness="1"
Margin="0 5 5 5"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Tracks}">
<DataGrid.Columns>
<DataGridTextColumn Header="Title" Binding="{Binding Title}"/>
<DataGridTextColumn Header="Index" Binding="{Binding Index}"/>
<DataGridTextColumn Header="Frame" Binding="{Binding Frame}"/>
</DataGrid.Columns>
</DataGrid>
... other code ...
Ok, so now that you see what I'm working with, I will say that this all works just fine. However I want to have all the binding taken care of in one place, and id like that place to be the XAML file. But I can't work out how to set the DataContext from inside the MainWindow.xaml. I see there is a Datacontext property for DataGrid and TextBox, but I can't figure out how to use them.
If you want any XAML element to refer to itself, you can use {RelativeSource}. To have the Window set itself as its own DataContext, just add the following to the opening tag of the Window:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
It appears that you cant bind a DataContext to a field, it has to be a property. Changing cueSheet to a property allowed me to bind it as the DataContext of my TextBox and DataGrid.
While
DataContext="{Binding cueSheet}"
didn't work because it was just a field, changing it to a property worked
DataContext="{Binding CueSheet}"
and I no longer need txtCueFilePath.DataContext = cueSheet; and dgCueTracks.DataContext = cueSheet; in the code-behind.

How come my databinding is writing out the Length property?

So I've setup a viewmodel to where it binds an ObservableCollection<string> to my DataGrid.
It prints out the value just fine but it also prints out the Length of the property? I don't recall ever setting that in any binding whatsoever. Why is it doing that?
My MainWindow.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MasterViewModel();
}
MasterViewModel.cs
class MasterViewModel
{
public Users Users { get; } = new Users();
public Achievements Achievements { get; } = new Achievements();
}
Users.cs
class Users : INotifyPropertyChanged
{
public Users()
{
newList.Add("Hello there");
}
private ObservableCollection<string> newList = new ObservableCollection<string>();
public ObservableCollection<string> NewList
{
get { return newList; }
set { newList = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<DataGrid ItemsSource="{Binding Users.NewList}" Width="400" Height="200" Margin="182,158,210,61">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
DataGrid has property AutoGenerateColumns which is set to True by default and makes DataGrid to create a column for each property defined in items.
DataGrid is bound to NewList which contains items of type string which has Length property. So it makes Length column
you can disable auto-generation by setting <DataGrid AutoGenerateColumns="False" ...
I forgot to add the property AutoGenerateColumns="False".
Not sure why it was set to true by default or why it woudl choose the length property of all the properties but I guess I got it fixed.

DataGridComboBoxColumn Binding to a List<String>

I have a WPF application that contains a datagrid. The datagrid is bound to my object OrderBlock which contains a List of type Orders.
<DataGrid DataContext="{Binding OrderBlock}"
Name="dataGridOrdersGood"
ItemsSource="{Binding Orders}"
This works fine and displays nicely in my datagrid. There is one property (StatusGood) in my List though that I would like to display as a combobox where there can be only two values, "Send" or "Hold".
So I was trying to bind the combobox values to the List StatusList as shown below. Then trying to bind the actual value to my object.
public class ViewModel : INotifyPropertyChanged
{
public List<string> StatusList;
// constructor
public ViewModel()
{
StatusList = new List<string>();
StatusList.Add("Hold");
StatusList.Add("Send");
}
}
<DataGridComboBoxColumn Header="Status Good" SelectedItemBinding="{Binding StatusList}" SelectedValuePath="{Binding StatusGood}"/>
However nothing is displayed other than a empty combobox. I do not understand why at the very least my combobox is not showing the value of my object? I am providing a list so again I do not understand why it's not showing anything.
I'm new to WPF and must struggling to understand it. I have referenced but obviously not fully understand it. http://msdn.microsoft.com/en-us/library/system.windows.controls.datagridcomboboxcolumn.aspx
Any help would be great!
Thanks,
M
I have a solution, where your List is a ComboBoxItem, would this be possible?
Here is my sample XAML:
<DataGrid AutoGenerateColumns="False" Name="myGridTest">
<DataGrid.Columns>
<DataGridTextColumn Header="Text" Binding="{Binding MyText}" />
<DataGridTemplateColumn Header="Combobox">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedIndex="0" ItemsSource="{Binding ComboList}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
My C#-Class
public class Test
{
private string _MyText;
private IList<ComboBoxItem> _ComboList;
public Test()
{
_MyText = "Test 123";
_ComboList = new List<ComboBoxItem>();
_ComboList.Add(new ComboBoxItem() { Content = "Next", IsSelected = true });
_ComboList.Add(new ComboBoxItem() { Content = "Prev." });
}
public IList<ComboBoxItem> ComboList
{
get { return _ComboList; }
set { _ComboList = value; }
}
public string MyText
{
get { return _MyText; }
set { _MyText = value; }
}
}
And for Testing:
List<Test> cList = new List<Test>();
cList.Add(new Test());
cList.Add(new Test());
cList.Add(new Test());
cList.Add(new Test());
cList.Add(new Test());
myGridTest.ItemsSource = cList;
I hope this help you...
It looks like DataGridComboBoxColumn->SelectedItemBinding has to be in your case:
SelectedItemBinding="{Binding StatusGood}"
and you have to set also ItemsSource property of the DataGridComboBoxColumn and modify your ViewModel for providing combo values to use property(StatusList) instead of field.
VM:
public class ViewModel
{
public List<string> StatusList { get; set; }
// constructor
public ViewModel()
{
StatusList = new List<string>();
StatusList.Add("Hold");
StatusList.Add("Send");
}
}
XAML:
<DataGrid.Resources>
<local:ViewModel x:Key="ComboItems" />
</DataGrid.Resources>
<DataGridComboBoxColumn SelectedItemBinding="{Binding StatusGood}" ItemsSource="{Binding Path=StatusList, Source={StaticResource ComboItems}}" >

Binding Datagrid to ObservableCollection<T> in SIlverlight

I am struggling to bind a DataGrid to an ObservableCollection in SIlverlight.
My very simple code is below. It currently shows a blank DataGrid. I have gone through tutorials etc and I am sure I am missing something very very basic.
Main Page XAML
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="Tower.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot">
<sdk:DataGrid Grid.Row="1" Margin="10" IsReadOnly="True" ColumnWidth="120" ItemsSource="{Binding Path=Tests, Mode=OneWay}" AllowDrop="True" />
</Grid>
Main Page Code Behind:
public partial class MainPage : UserControl
{
public ObservableCollection<Test> Tests { get; set; }
public MainPage()
{
InitializeComponent();
DataContext = this;
Tests = new ObservableCollection<Test>();
Tests.Add(new Test() { Label = "Test1" });
Tests.Add(new Test() { Label = "Test2" });
Tests.Add(new Test() { Label = "Test3" });
Tests.Add(new Test() { Label = "Test4" });
}
}
Test Class:
public class Test : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private String _Label;
public String Label
{
get
{
return _Label;
}
set
{
_Label = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Label"));
}
}
}
There are two issues in your code -
You can only bind with property and not with fields. so make Test property first.
Second, you need to set the DataContext to self for binding to work.
public partial class MainPage : UserControl
{
public ObservableCollection<Test> Tests { get; set; }
public MainPage()
{
InitializeComponent();
DataContext = this;
Tests = new ObservableCollection<Test>();
Tests.Add(new Test() { Label = "Test1" });
Tests.Add(new Test() { Label = "Test2" });
Tests.Add(new Test() { Label = "Test3" });
Tests.Add(new Test() { Label = "Test4" });
}
}
XAML -
<Grid x:Name="LayoutRoot">
<sdk:DataGrid Grid.Row="1" Margin="10" IsReadOnly="True" ColumnWidth="120"
ItemsSource="{Binding DataContext.Tests,
RelativeSource={RelativeSource FindAncestor,
AncestorType= UserControl}}" AllowDrop="True" />
</Grid>
Notice propertyName - It should be Tests and not tests. This is just a side-note, follow the naming conventions of Microsoft. Property name's first letter should be uppercase always.
For binding to work Tests has to be a public property. (I was surprised to see that the property needed to be public but could not get it to work without)
For referenceing the property in the binding you must either
Set the datacontext like RV suggested
Or reference like this:
<Grid>
<DataGrid ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=Tests}" />
</Grid>
Don't forget :
PUBLIC ObservableCollection tests {get; private set}

WPF Gridview Checkbox Column Header MVVM

I am still wrapping my head around this whole MVVM pattern but i thought i had a good grasp on it until i attempted to create a Checkbox column for my Gridview. I need the user to be able to select all items listed(via the header checkbox) or select the items listed individually. I databound the IsChecked property of my checkboxes to two boolean fields on my viewmodel. The checkbox on the cell template works as expected and fires the property changed event. The header does nothing. What am i missing here. Again this is still new to me so be gentle. Also if there is something i should be doing, or a better way to accomplish this...im all ears.
Thanks
XAML
<UserControl x:Class="CheckBoxDemo.GridDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ListView ItemsSource="{Binding PersonList}">
<ListView.View>
<GridView>
<GridViewColumn Width="50">
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsMainSelected}"/>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
ViewModel
class GridDemoViewModel:INotifyPropertyChanged
{
public List<Person> PersonList { get; private set; }
// Fields...
private bool _isMainSelected;
public bool IsMainSelected
{
get { return _isMainSelected; }
set
{
_isMainSelected = value;
NotifyPropertyChanged("IsMainSelected");
}
}
public GridDemoViewModel()
{
PersonList = new List<Person>();
PersonList.Add(new Person { Name = "John"});
PersonList.Add(new Person { Name = "Tom" });
PersonList.Add(new Person { Name = "Tina" });
PersonList.Add(new Person { Name = "Mary" });
PersonList.Add(new Person { Name = "Mia"});
PersonList.Add(new Person { Name = "Crystal" });
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Person Class
public class Person:INotifyPropertyChanged
{
public string Name { get; set; }
// Fields...
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value)
return;
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
The issue is that the GridViewColumn is not part of the Visual Tree. This means that it does not inherit the DataContext of the parent ListView. You have to find some other way of referencing the ViewModel. Check out Josh Smith's DataContextSpy which allows you to easily introduce an "artificially inherited" DataContext
<UserControl.Resources>
<spy:DataContextSpy x:Key="Spy" />
</UserControl.Resources>
<DataTemplate>
<CheckBox IsChecked="{Binding Source={StaticResource Spy} Path=DataContext.IsMainSelected}"/>
</DataTemplate>

Categories