content does not appear in datagrid - c#

Well, I have a data class with some data objects like this:
private ObservableCollection<bool> check = new ObservableCollection<bool>();
public ObservableCollection<bool> Check
{
get { return check; }
set
{
check = value;
Notify("check");
}
}
private ObservableCollection<string> user = new ObservableCollection<string>();
public ObservableCollection<string> User
{
get { return user; }
set
{
user = value;
Notify("user");
}
}
And in the MainWindow I added a DataGrid like this:
<DataGrid AutoGenerateColumns="False"
Name="dataGrid1"
CanUserAddRows="False" CanUserSortColumns="False" CanUserResizeColumns="True" CanUserReorderColumns="False"
ItemsSource="{Binding}">
<DataGrid.Columns >
<DataGridCheckBoxColumn Header = "" Binding="{Binding Check, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" MinWidth="50" />
<DataGridTextColumn Header = "User" Binding="{Binding User, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" MinWidth="50" />
</DataGrid.Columns>
</DataGrid>
For the whole Window the datakontext ist set to the data class. In the constructor I called "DataContext = theData"; I added some values in the constructor of the data class and proofed by running the program the instance of this class. The values are correct added to the ObservableCollection.
But the values are not shown in the datagrid. Why?

The ItemsSource property of the DataGrid should be set or bound to an IEnumerable<T>. And a single column in the DataGrid should be bound to a property of the type T. You are trying to bind a DataGridTextColumn to an ObservableCollection<string> and a DataGridCheckBoxColumn to an ObservableCollection<bool> and this makes no sense. They should be bound to a string and bool property respectively. Please refer to the following sample code.
Model:
public class YourDataObject : INotifyPropertyChanged
{
private bool _check;
public bool Check
{
get { return _check; }
set { _check = value; NotifyPropertyChanged(); }
}
private string _user;
public string User
{
get { return _user; }
set { _user = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
View Model:
public class ViewModel
{
public ViewModel()
{
TheDataObjects = new ObservableCollection<YourDataObject>();
TheDataObjects.Add(new YourDataObject());
TheDataObjects.Add(new YourDataObject());
TheDataObjects.Add(new YourDataObject());
}
public ObservableCollection<YourDataObject> TheDataObjects { get; private set; }
}
View:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
<DataGrid AutoGenerateColumns="False"
Name="dataGrid1"
CanUserAddRows="False" CanUserSortColumns="False" CanUserResizeColumns="True" CanUserReorderColumns="False"
ItemsSource="{Binding TheDataObjects}">
<DataGrid.Columns >
<DataGridCheckBoxColumn Header = "" Binding="{Binding Check, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" MinWidth="50" />
<DataGridTextColumn Header = "User" Binding="{Binding User, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" MinWidth="50" />
</DataGrid.Columns>
</DataGrid>

Try setting,
this.DataContext = theData;

You need to set proper property to ItemsSource.
ItemsSource="{Binding User}"
Above line will clear the issue.
Also, you should Notify public properties in Setter.
Notify("Check");
Notify("User");

Related

WPF C# Display Observable Collection in DataGrid with Binding

i know that was asked many times before, but i cant find my error, why it dont work. I set they DataContext in Xaml. Would it be better in de Code behind?
I want to Display A ObservableCollection in an DataGrid, where i can select multiple Rows, which I get back to my viewmodel.
ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<Projekt> Projekts { get; set; }
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged();
}
}
private string _bez;
public string Bezeichnung
{
get { return _bez; }
set
{
_bez = value;
NotifyPropertyChanged();
}
}
private string _number;
public string Nummer
{
get { return _number; }
set
{
_number = value;
NotifyPropertyChanged();
}
}
}
Xaml:
<Window.DataContext>
<kbwViewModels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<DataGrid x:Name="dg_Projekt" HorizontalAlignment="Left" Height="238" Margin="10,52,0,0" VerticalAlignment="Top" Width="425" SelectionMode="Extended" BorderBrush="White" Background="Gray" ItemsSource="{Binding Projekts}"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTextColumn Header="Nummer" Binding="{Binding Nummer}" FontFamily="Arial"/>
<DataGridTextColumn Header="Projekt Name" Binding="{Binding Bezeichnung}" FontFamily="Arial" />
<DataGridTextColumn Header="Status" Binding="{Binding Status}" FontFamily="Arial"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
MainWindow:
private Common common;
private MainWindowViewModel viewModel;
public MainWindow()
{
InitializeComponent();
string connectionDb = ConfigurationManager.ConnectionStrings["KBWTime"].ConnectionString;
common = new Common(connectionDb);
viewModel = new MainWindowViewModel();
viewModel.Projekts = new ObservableCollection<Projekt>(common.GetProjektList());
}
ObservableCollection only emits an event when the collection changes (new element, remove element, etc.). However you are change a collection with other here:
viewModel.Projekts = new ObservableCollection<Projekt>(common.GetProjektList());
So you have two options:
A) Add each item to the ObservableCollection
foreach(var item in common.GetProjektList())
viewModel.Projekts.Add(item);
B) Create a setter for Projekts, and notify when this property changes

WPF Binding DataGridCheckBoxColumn

I am having difficulty binding a DataGridCheckBoxColumn in a DataGrid in WPF.
What I am trying to do is have a "Select All" button to check all the items in the grid.
<Button Grid.Row="1" Grid.Column="0" Content="Select All In List" HorizontalAlignment="Stretch" Command="{Binding SelectAll}"></Button>
In my ViewModel I have a Command that is called from the button.
public ICommand SelectAll { get; set; }
private void OnSelectAll(object obj)
{
foreach (var item in EducationLeaflets)
{
item.Selected = true;
}
OnPropertyChanged("EducationLeaflets");
}
This is my property from my ViewModel that I bind my DataGrid to:
public ObservableCollection<LeafletListModel> EducationLeaflets { get; private set; }
My DataGrid with a DataGridCheckBoxColumn as the first column.
<DataGrid Grid.Row="0" Grid.Column="0"
AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding EducationLeaflets}"
RowDetailsVisibilityMode="VisibleWhenSelected"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.ColumnSpan="3" Background="White" HorizontalGridLinesBrush="#FFF0F0F0" VerticalGridLinesBrush="#FFF0F0F0">
<DataGrid.Columns>
<DataGridCheckBoxColumn
Binding="{Binding Path=Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</DataGridCheckBoxColumn>
<DataGridTextColumn
Binding="{Binding Id}"
Header="RecordId"
Width="SizeToHeader" />
<DataGridTextColumn
Binding="{Binding Name}"
Header="Name"
Width="*" />
</DataGrid.Columns>
</DataGrid>
Also the model that is displayed in each grid row.
public class LeafletListModel: ListModel
{
public LeafletListModel(int id, string name, DateTime bpsDrugsUpdated):base(id, name)
{
BpsDrugsUpdated = bpsDrugsUpdated;
}
public bool Selected { get; set; }
public DateTime BpsDrugsUpdated { get;private set; }
}
When I click the button the items in the DataGrid are not checked as I would like. Thank you for your help.
It is not EducationLeaflets that changes - it stays the same ObservableCollection as before clicking SelectAll. Even its content does not change (this would be reflected in the CollectionChanged event from the ObservableCollection.
What actually changes are the individual items in the ObservableCollection. And since these do not implement INotifyPropertyChanged, the update will not be reflected in the Views.
So, if you make LeafletListModel implement INotifyPropertyChanged, it should
work as expected.
public class LeafletListModel: ListModel, INotifyPropertyChanged
{
private bool _selected;
public LeafletListModel(int id, string name, DateTime bpsDrugsUpdated):base(id, name)
{
BpsDrugsUpdated = bpsDrugsUpdated;
}
public bool Selected
{
get { return _selected; }
set
{
if (_selected != value)
{
_selected = value;
OnPropertyChanged();
}
}
}
public DateTime BpsDrugsUpdated { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

WPF MVVM DataGrid filtering using ComboBox

I have a WPF MVVM application with a DataGrid and a ComboBox that are binded to the same List of entities in a ViewModel class.
I want to filter the DataGrid entries through the ComboBox selection, what is the proper way to do this? Since I'm working with MVVM, I want to achieve this with data bindings, and avoid useless code behind.
My XAML code is like the following
<DataGrid ItemsSource="{Binding Posts}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Posts}"
DisplayMemberPath="Blog.Url" />
ViewModel
public class MainWindowViewModel
{
private SqliteDbContext context;
public List<Post> Posts { get; set; }
public MainWindowViewModel()
{
context = new SqliteDbContext();
Posts = context.Posts.Include(p => p.Blog).ToList();
}
}
In addition, with this code my ComboBox shows duplicates of Urls, how can I distinct these values?
Thanks.
This should do the trick.
ViewModel:
public class MainWindowViewModel
{
private SqliteDbContext context;
public ObservableCollection<Post> Posts { get; set; }
private string _selectedUrl;
public ICollectionView PostsView { get; set; }
public MainWindowViewModel()
{
context = new SqliteDbContext();
Posts = new ObservableCollection<Post>(context.Posts.Include(p => p.Blog));
PostsView = new CollectionViewSource { Source = Posts }.View;
PostsView.Filter = post => SelectedUrl == null || SelectedUrl == ((Post)post).Blog.Url;
}
public string SelectedUrl
{
get
{
return _selectedUrl;
}
set
{
_selectedUrl = value;
PostsView.Refresh();
}
}
}
XAML:
<DataGrid ItemsSource="{Binding PostsView}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Posts}"
DisplayMemberPath="Blog.Url"
SelectedValuePath="Blog.Url"
SelectedValue="{Binding SelectedUrl}"/>
You could bind the ComboBox to a collection of the unique urls that you create in the view model.
You could then filter the DataGrid by binding the SelectedItem property of the ComboBox to a source property of the view model that filters the Posts source Collection.
Please refer to the following code sample.
View Model:
public class MainWindowViewModel : INotifyPropertyChanged
{
private readonly SqliteDbContext context;
private readonly List<Post> _allPosts;
public MainWindowViewModel()
{
context = new SqliteDbContext();
_allPosts = context.Posts.Include(p => p.Blog).ToList();
_posts = _allPosts;
Urls = _allPosts.Where(p => p.Blog != null && !string.IsNullOrEmpty(p.Blog.Url)).Select(p => p.Blog.Url).ToList();
}
private List<Post> _posts;
public List<Post> Posts
{
get { return _posts; }
set { _posts = value; NotifyPropertyChanged(); }
}
public List<string> Urls { get; set; }
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value; NotifyPropertyChanged();
Posts = _allPosts.Where(p => p.Blog != null && p.Blog.Url == _url).ToList();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
View:
<DataGrid ItemsSource="{Binding Posts}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTextColumn Header="BlogUrl" Binding="{Binding Blog.Url}" />
</DataGrid.Columns>
</DataGrid>
<ComboBox ItemsSource="{Binding Urls}" SelectedItem="{Binding Url}" />

Binding a DataGrid TwoWay to an ObservableList of objects

I have a class to store data:
public enum ColumnType
{
...
}
public class LogColumn
{
public string Name { get; set; }
public ColumnType Type { get; set; }
public bool OrderBy { get; set; }
}
and a ObservableCollection of it in my UserControl:
private ObservableCollection<LogColumn> _logColumns = new ObservableCollection<LogColumn>();
public ObservableCollection<LogColumn> LogColumns
{
get { return _logColumns; }
set
{
_logColumns = value;
OnPropertyChanged("LogColumns");
}
}
This collection is bound to a DataGrid:
<DataGrid Name="dgColumnSelection"
ItemsSource="{Binding LogColumns, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True}" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" IsReadOnly="True" Binding="{Binding Name, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<DataGridComboBoxColumn Header="Type" ItemsSource="{Binding Source={StaticResource ColumnType}}" SelectedValueBinding="{Binding Type, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<DataGridCheckBoxColumn Header="Order by" Binding="{Binding OrderBy, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</DataGrid.Columns>
</DataGrid>
But neither PropertyChanged-event nor Validation is fired. Where is my fault?
Thx.
Best regards
The event is never fired because you are never setting the LogColumns variable to something new. What you are doing is changing properties on the LogColumn items contained within the LogColumns collection.
Implementing the INotifyPropertyChanged interface on your LogColumn class will might help do what you want.
ObservableCollection only notify if the items of ObservableCollection get chagned not the items item get changed to do so you need to implement the inotifyproprty chagned in your logcolumns class
as
public class LogColumn : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; Onchanged("Name"); }
}
private ColumnType _Type;
public ColumnType Type
{
get { return _Type; }
set { _Type = value; Onchanged("Type"); }
}
private bool _OrderBy;
public bool OrderBy
{
get { return _OrderBy; }
set { _OrderBy = value; Onchanged("OrderBy"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void Onchanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
now this will work.

How to set the ItemSource of a Datagrid to a collection inside an object using MVVM

I have a ComboBox that holds a list of StrategyViewModels. The StrategyViewModel has an ObservableCollection of StrategyParameterViewModels inside of it. I have a StrategyViewModel called SelectedStrategy that I bound to the SelectedItem property on the combobox. When the user selects a Strategy from the ComboBox I would like to set the itemsource of a datagrid to the StrategyParameters inside that Strategy. I've tried all different ways, but nothing seems to work.
Here's the XAML:
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" ItemsSource="{Binding Strategies}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedStrategy,Mode=TwoWay}" />
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings" ItemsSource="{Binding SelectedStrategy.Parameters, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
Here is the Strategy ViewModel:
public class StrategyViewModel : ViewModelBase
{
public StrategyObject Strategy { get; set; }
public int Id
{
get { return Strategy.Id; }
set
{
if (value == Strategy.Id)
return;
Strategy.Id = value;
OnPropertyChanged("Id");
}
}
public string Name
{
get { return Strategy.Name; }
set
{
if (value == Strategy.Name)
return;
Strategy.Name = value;
OnPropertyChanged("Name");
}
}
public ObservableCollection<StrategyParameterViewModel> Parameters { get { return _parameters; } }
public ObservableCollection<StrategyParameterViewModel> _parameters;
public StrategyViewModel()
{
Strategy = new StrategyObject();
_parameters = new ObservableCollection<StrategyParameterViewModel>();
}
public StrategyViewModel(StrategyObject o, IEnumerable<StrategyParameterObject> pms)
{
Strategy = o;
_parameters = new ObservableCollection<StrategyParameterViewModel>();
foreach (StrategyParameterObject s in pms)
{
_parameters.Add(new StrategyParameterViewModel(s));
}
}
}
And here is the StrategyParameter ViewModel:
public class StrategyParameterViewModel : ViewModelBase
{
public StrategyParameterObject StrategyParameter { get; set; }
public int Id
{
get { return StrategyParameter.Id; }
set
{
if (value == StrategyParameter.Id)
return;
StrategyParameter.Id = value;
OnPropertyChanged("Id");
}
}
public int StrategyId
{
get { return StrategyParameter.StrategyId; }
set
{
if (value == StrategyParameter.StrategyId)
return;
StrategyParameter.StrategyId = value;
OnPropertyChanged("StrategyId");
}
}
public string Name
{
get { return StrategyParameter.Name; }
set
{
if (value == StrategyParameter.Name)
return;
StrategyParameter.Name = value;
OnPropertyChanged("Name");
}
}
public string Value
{
get { return StrategyParameter.Value; }
set
{
if (value == StrategyParameter.Value)
return;
StrategyParameter.Value = value;
OnPropertyChanged("Value");
}
}
public StrategyParameterViewModel()
{
StrategyParameter = new StrategyParameterObject();
}
public StrategyParameterViewModel(StrategyParameterObject o)
{
StrategyParameter = o;
}
}
The problem is that you are trying to set up a two way binding with a read-only property. You need to either add a setter for SelectedStrategy.Parameters, or use a OneWay binding on the datagrid itemssource.
Off the top of my head, I don't think you're doing anything wrong with regards to your XAML.
Data Binding is notoriously tricky. I suggest adding PresentationTraceSources to get more debug information as to what is being found. This will ship several lines of data about binding results to your Output window.
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<Binding Path="Strategies" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.ItemsSource>
<ComboBox.SelectedItem>
<Binding Path="SelectedStrategy" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.SelectedItem>
</ComboBox>
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings">
<DataGrid.ItemsSource>
<Binding Path="SelectedStrategy.Parameters" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
In addition, WPF Inspector can help, as you can tweak Data Binding expressions in real time when running your actual app. http://wpfinspector.codeplex.com/

Categories

Resources