I have a DataGrid which displays a list of objects. One of the properties in the objects is another custom object. This object is shown as a ComboBox in the grid. When I change the selected item in the ComboBox from the grid, everything seems to work as I expect. However, when I change the selected item from code behind, the SelectedItem in the ComboBox doesn't update. I have implemented the INotifyPropertyChanged and the event is firing as it should. I have also tried to print the domain name in a TextBox and it shows the correct value. The problem I have is that the SelectedItemBinding doesn't seem to work when I update from code behind. Can anyone explain why? Here is my code:
XAML
<DataGrid AutoGenerateColumns="False" Height="506" HorizontalAlignment="Left" Name="EntityGrid" VerticalAlignment="Top" Width="360" Margin="10,62,0,0">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding dbName, Mode=TwoWay}" />
<DataGridComboBoxColumn x:Name="Domain" Header="Domain" SelectedItemBinding="{Binding Domain, Mode=TwoWay}" DisplayMemberPath="DomainName}"/>
<DataGridCheckBoxColumn Header="Static" Binding="{Binding Static}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
My objects
public class Entity : INotifyPropertyChanged
{
private string _dbName;
public string dbName { get { return _dbName; } set { _dbName = value; NotifyPropertyChanged("dbName"); } }
public string EntityName { get; set; }
private Domain _domain;
public Domain Domain
{
get { return _domain; }
set
{
_domain = value;
NotifyPropertyChanged("Domain");
}
}
public bool Static { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Domain : INotifyPropertyChanged
{
public string DomainName { get; set; }
public string ContextName { get; set; }
public string FileName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Init code
List<Domain> domains = getDomainsFromConfig();
List<Entity> Entities = dal.getAllEntities(config.Paths.dbml.AllEntities, config.Paths.dbml.LockedEntities);
EntityGrid.ItemsSource = Entities;
Domain.ItemsSource = domains;
Update code
foreach (Entity entity in Entities)
{
entity.Domain = getDefaultDomain();
}
Solved the problem after a couple hours of head ache. In my update code, I created a new instance of Domain. My guess is that it made it inequal to the objects in the current itemsource. My solution to the problem was to select the Domain from the original Domain ItemSource and thereafter assign it to the Entity.
try this:
change the list to ObservableCollection
List<Domain> to ObservableCollection<Domain>
change the combobox to
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="domain">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
cause the Domain object have many properties you should set the selected value to the prmary key of the Domain object and the displaymemberpath to the value that you want to show - i think in you case the Domain Name.
Related
I'm using a datagrid, bound to an observablecollection with TwoWay binding.
My goal is, that a user generates a list of data, starting from an empty collection.
So I enabled the option CanUserAddRow.
In the code, I generate the obsevrable collection with the following code:
private ObservableCollection<Ticket> idlessTicketList = new ObservableCollection<Ticket>();
The Ticket class, which the ObservableCollection consists of, looks as follows:
public class Ticket
{
public Ticket() { }
public bool ticketUsed { get; set; }
public string ticketNumber { get; set; }
public string ticketCustomer { get; set; }
public string ticketText { get; set; }
public double ticketTime { get; set; }
public Decimal ticketTypeNr { get; set; }
public string ticketTypeText { get; set; }
}
In the MainWindow Method I set the itemSource of my Datagrid to my ObservableCollection:
public MainWindow()
{
InitializeComponent();
gridIdlessTickets.ItemsSource = idlessTicketList;
}
My problem is now, that the empty row to add a new row is not displayed at startup.
If I add a new row by code myGridd.Add(row), then the empty row is displayed correctly and everythings works a expected.
How must the ObservableCollection be initialized and referenced to the itemSource correctly?
Where is the best place to initialize an itemSource?
Thanks in advance
This should work for you. Let me know if it helped:
XAML:
<Window>
<Grid>
<Datagrid ItemsSource="{Binding idlessTicketList }" SelectionMode="Single" SelectionUnit="Cell" IsReadOnly="False"
CanUserAddRows="True" CanUserDeleteRows="False" AutoGenerateColumns="False">
//ColumnDefinition...etc
<DataGridTextColumn Header="TicketNumber" Binding="{Binding TicketNumber}" />
<DataGridTextColumn Header="TicketCustomer" Binding="{Binding TicketCustomer}"
</Datagrid>
<Button name="ThisIsYourUpdateButton" Command="{Binding UpdateMyTicket}" Width="200" Content="Update me now"/>
</Grid>
</Window>
Code Behind (.xaml.cs):
public MainWindow()
{
InitializeComponent(); //parses the XAML...
DataContext = new MainWindowViewModel(); //outsources the view code to the
//"controller" to make the view only display and nothing else...
}
ViewModel: (MainWindowViewModel.cs)
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace YourNameSpace
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand UpdateMyTicket => new DelegateCommand<object>(ExecuteUpdateTicket);
public Ticket TicketInstance {get;set;}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<Ticket> _idlessTicketList;
public ObservableCollection<Ticket> idlessTicketList
{
get { return _idlessTicketList; }
set
{
_idlessTicketList = value;
OnPropertyChanged("idlessTicketList");
}
}
//Constructor
public MainWindowViewModel()
{
idlessTicketList = new ObservableCollection<Ticket>();
}
public void ExecuteUpdateTicket(obj param)
{
//if the button is clicked you update your Ticket class properties here!
}
}
}
Add DelegateCommand.cs class like this:
using System;
namespace YourNamespaceName
{
public class DelegateCommand<T> : System.Windows.Input.ICommand
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public DelegateCommand(Action<T> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public void Execute(object parameter)
{
_execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
With a seperate View-Model Class which initializes the observablecollection, the empty row is displayed correctly.
But the data is not updated anymore.
Here's what I did:
I added the new class as a new namesspace
xmlns:vm="clr-namespace:nsMainWindowViewModel"
The binding is written like that:
<DataGrid ItemsSource="{Binding Path=idlessTicketList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketText, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketTime, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:n2\}}"/>
</DataGrid.Columns>
</Datagrid>
With this implementation, the PropertyChangedEventHandler is called when the application starts, but not wen a element changes.
How must the binding be written?
For the datagrid i tried:
{Binding Path=(idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:MainWindowViewModel.idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
and for the columns I tried;
{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:ticketCustomer), UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:MainWindowViewModel.ticketCustomer), UpdateSourceTrigger=PropertyChanged}
Whit the debugging, I can see that the PropertyChange Method is called initially, but not after editing single elements.
How must the binding be defined to update te observableCollection in another namespace?
Thanks in advance
The SelectedItem property binding is not causing its DataGrid row to be highlighted on initial load.
I have a DataGrid with a binding on SelectedItem that is not getting highlighted until I click it again. I think I have an order of operations problem--coming from the fact that all the ViewModel code runs before the Views get rendered. It works fine once I click a row (even the same one that's already in the SelectedAccount prop), but I need to able to highlight a row from the VM.
I can easily verify the SelectedAccount property is not null because there are other ViewModels that display it's values via PubSubEvents.
I've tried several solutions methods, and the only way I've been able to get it to work so far just feels dirty:
using ApplicationName.UI.ViewModels;
public partial class AccountsView : UserControl
{
public AccountsView()
{
InitializeComponent();
Loaded += AccountsView_Loaded;
}
private void AccountsView_Loaded(object sender, RoutedEventArgs e)
{
AccountsViewModel viewModel = (AccountsViewModel)DataContext;
AccountsDataGrid.SelectedItem = viewModel.SelectedAccount;
AccountsDataGrid.Focus();
UpdateLayout();
}
}
I don't like this because it causes the OnPropertyChanged event to fire twice, once before the views load, and and again after the above hack. This triggers a SQL call, so I want to avoid that. I also thought the point of MVVM was to decouple the view from the viewmodel, but maybe I'm not understanding that as well as I thought.
Here's the XAML for the DataGrid:
<ScrollViewer Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="5,0">
<DataGrid Name="AccountsDataGrid"
ItemsSource="{Binding Accounts}"
SelectedItem="{Binding SelectedAccount}"
AutoGenerateColumns="False"
CanUserResizeColumns="True"
SelectionMode="Single"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTextColumn Header="ClinicId"
TextBlock.TextAlignment="Center"
Width="75"
Binding="{Binding ClinicId}" />
<DataGridTextColumn Header="Account#"
Width="75"
Binding="{Binding AccountNumber}"/>
<DataGridTextColumn Header="LastName"
Width="1*"
Binding="{Binding LastName}" />
<DataGridTextColumn Header="FirstName"
Width="1*"
Binding="{Binding FirstName}" />
<DataGridTextColumn Header="Balance"
Width="Auto"
Binding="{Binding Balance}" />
<DataGridTextColumn Header="Follow Up"
Width="100"
Binding="{Binding FollowUpDate}"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
And the intial Load method in the ViewModel, which is where I want to set the highlighted row.
public void Load()
{
RefreshGrid();
SelectedAccount = Accounts.First();
_accountId = SelectedAccount.Id;
}
EDIT
The issue was subtle, but makes perfect sense now.
private Account _selectedAccount;
public Account SelectedAccount
{
get => _selectedAccount;
set => SetSelectedAccount(value);
}
private void SetSelectedAccount(Account value)
{
_selectedAccount = value;
OnPropertyChanged("_selectedAccount"); // <= whoops
if (_selectedAccount != null)
OnAccountSelected(
_selectedAccount.PrimaryKeyFields);
}
Raising this event for a private property doesn't make sense, as the view cannot see it, and is bound to SelectedAccount. Changing it to OnPropertyChanged("SelectedAccount") did the trick.
Implementing INotifyPropertyChanged should be enough, this code works on my end, I'm using a Command to call the Load() method but it's probably not needed in your code.
ViewModel and C# code :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
Accounts = new List<Account>();
Accounts.AddRange(
Enumerable.Range(0, 10)
.Select(r => new Account
{
AccountNumber = r,
FirstName = $"First{r}",
LastName = $"Last{r}"
}));
LoadedCommand = new WpfCommand((param) => Load());
}
private void Load()
{
SelectedAccount = Accounts.FirstOrDefault(a => a.AccountNumber == 2);
}
public WpfCommand LoadedCommand { get; set; }
public List<Account> Accounts { get; set; }
private Account _selectedAccount = null;
public Account SelectedAccount
{
get { return _selectedAccount; }
set
{
_selectedAccount = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedAccount)));
}
}
}
public class Account
{
public int AccountNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class WpfCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public WpfCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute?.Invoke(parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke(parameter) ?? true;
}
}
XAML :
<!--System.Windows.Interactivity.WPF nuget package-->
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<DataGrid
ItemsSource="{Binding Accounts}"
SelectedItem="{Binding SelectedAccount}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding AccountNumber}"/>
<DataGridTextColumn Binding="{Binding LastName}" />
<DataGridTextColumn Binding="{Binding FirstName}" />
</DataGrid.Columns>
</DataGrid>
In your view model, use your framework's RaisePropertyChanged(); function (or whatever the equivalent is). In the code-behind of the DataGrid element, try this code:
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
for (int i = 0; i < DataGrid.Items.Count; i++)
{
DataGridRow row = (DataGridRow)DataGrid.ItemContainerGenerator.ContainerFromIndex(i);
TextBlock cellContent = DataGrid.Columns[0].GetCellContent(row) as TextBlock;
if (cellContent != null && cellContent.Text.Equals(DataGrid.SelectedItem))
{
object item = DataGrid.Items[i];
DataGrid.SelectedItem = item;
DataGrid.ScrollIntoView(item);
row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
break;
}
}
}
In my example, I used a generic string list of names, so you may need to alter the line TextBlock cellContent = DataGrid.Columns[0].GetCellContent(row) as TextBlock; and cellContent.Text.Equals(DataGrid.SelectedItem)) to meet your line selection criteria.
The other alternative if you don't want to use the code-behind is an attached behavior that more-or-less does the same thing.
the following code I wrote for two way binding. The UI gets updated when anything from code changes but vice versa doesn't work, the UI doesn't change the data code when the checkbox is clicked by the user. Appreciate if anybody sheds some light on the solution.
XAML Code
<DataGrid ItemsSource="{Binding StatusItems}" Name="DataGridUploadingRevitFiles" Margin="5"
IsReadOnly="False" SelectionMode="Single" CanUserAddRows="True"
AutoGenerateColumns="False" SelectionUnit="Cell" Height="Auto">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Update" Width=".5*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="200"
IsChecked="{Binding Path=IsUpdateAbleFile,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Model [FamilyStatusItem.cs]
public class FamilyStatusItem : INotifyPropertyChanged
{
private bool _isUpdateAbleFile;
public bool IsUpdateAbleFile
{
get => this._isUpdateAbleFile;
private set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel [FamilyStatusViewItem.cs]
public class FamilyStatusViewItem
{
public ObservableCollection<FamilyStatusItem> StatusItems { get; set; }
public FamilyStatusViewItem()
{
this.StatusItems = new ObservableCollection<FamilyStatusItem>();
}
}
Your setter is private which means it can’t be called from the outside. Thus when you tick or untick the checkbox it can’t be called and the property retains the old state.
Solution: Remove the private modifier.
Try to use public setter
public bool IsUpdateAbleFile
{
get => this._isUpdateAbleFile;
set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
}
You are having a private setter
private set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
Change this to a public, then it should work.
I am working with mvvmLight-framework. I got two usercontrols.
One usercontrol (objectInspector) is for saving data:
<Button Command="{Binding ObjectModel.OkCommand}" />
It is bound to the view-model "objectInspectorViewModel".
The other usercontrol (outliner) is for loading/presenting all of the data
<DataGrid ItemsSource="{Binding Path=ObjectModels, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="0"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="ID"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Length}" Header="Length"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Height}" Header="Height"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Width}" Header="Width"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Type}" Header="Type"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
It is bound to the viewmodel "outlinerViewModel".
Loading and saving works fine. But what i want is to refresh the datagrid after saving a new object.
My OutlinerViewModel looks like this:
public class OutlinerViewModel : BaseViewModel
{
public List<ObjectModel> ObjectModels { get; set; }
public OutlinerViewModel()
{
string file = $#"{Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName}\DataSource\objects.csv";
ObjectModels = ReadFile(file);
}
[...]
}
My ObjectInspectorViewModel looks like this:
public class ObjectInspectorViewModel : BaseViewModel
{
public ObjectModel ObjectModel { get; set; } = new ObjectModel();
}
And this is the method for saving a new object to the ''database'' from the ObjectModel:
public RelayCommand OkCommand { get; private set; }
protected override void InitCommands()
{
base.InitCommands();
OkCommand = new RelayCommand(
() =>
writeToCsv(
$#"{Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName}\DataSource\objects.csv",
this.ToString()),
() => IsOk);
}
How to update dataGrid after saving data with MvvmLight?
Personnaly, I like to have my view up to date with the data in the database.
So when I save an element, I refresh the datagrid by retrieving the data from there.
This way, you don't have synchronizing issue. If performance is important, you can try to update only the element you saved.
So in your case, i'd just call the "readFile" method after saving an object.
By the way, all your ObjectModel properties should call RaisePropertyChanged in your ViewModel:
Something like this:
private long IdProperty;
public long Id
{
get { return IdProperty; }
set { IdProperty = value; RaisePropertyChanged(() => Id); }
}
private long NameProperty;
public long Name
{
get { return NameProperty; }
set { NameProperty = value; RaisePropertyChanged(() => Name); }
}
This way, all the properties are updated in the view when they're modified. (RaisePropertyChanged comes with the ViewModelBase class)
Change your List<ObjectModel> to an ObservableCollection<ObjectModel>.
It should be that simple.
Update
Also, raise PropertyChanged.
private ObservableCollection<ObjectModel> objectModels;
public ObservableCollection<ObjectModel> ObjectModels
{
get
{
return onjectModels;
}
set
{
objectModels = value;
OnPropertyChanged();
}
}
Assuming your view model implements INotifyPropertyChanged. It could be implemented like this.
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I just started a new project and used this occasion to get used to MVVM.
But I am faltering with a special case, where I try to bind a list of a custom class from my ObservableCollection to a Datagrid.
I use the following constellation and all I can accomplish is the Datagrid Column showing a Collection.
https://snag.gy/2MDEuS.jpg
If I try to advance further into the Object with {Binding Path=Supplier.Supplier} it aint working with the following error which indicates, the compiler is not able to read the property from the list, as I interpret the error:
System.Windows.Data Error: 40 :
BindingExpression path error:
'Supplier' property not found on 'object' ''List`1' (HashCode=55391087)'.
BindingExpression:Path=Supplier.Supplier;
DataItem='O_SupplierReport' (HashCode=61342683);
target element is 'TextBlock' (Name='');
target property is 'Text' (type 'String')
There are other textboxes, I can easily fill with Binding={Binding Path=MySelectedItem.SupplierName} for example.
Can you give me an advice on this?
//ViewModel
public class V_SupplierReport: INotifyPropertyChanged
{
public ObservableCollection<O_SupplierReport> _SupplierReports;
public O_SupplierReport MySelectedItem { get; set; }
private List<S_Supplier> _Supplier { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected internal void OnPropertyChanged(string propertyname)
{
if (!(PropertyChanged == null))
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
public ObservableCollection<O_SupplierReport> SupplierReports
{
get { return _SupplierReports; }
set { _SupplierReports = value; }
}
public V_SupplierReport()
{
this._SupplierReports = new ObservableCollection<O_SupplierReport>();
}
public int Lieferanten
{
get { return _Supplier; }
set
{
if (_Supplier == value) return;
_Supplier = value;
OnPropertyChanged("Supplier");
}
}
}
//Model
public class O_SupplierReport : INotifyPropertyChanged
{
public List<S_Supplier> Supplier { get; set; }
public O_SupplierReport(List<S_Supplier> sup)
{
this.Supplier = sup;
}
public event PropertyChangedEventHandler PropertyChanged;
protected internal void OnPropertyChanged(string propertyname)
{
if (!(PropertyChanged == null))
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
//Classpublic class S_Supplier
{
public int Supplier { get; set; }
public S_Supplier(int sup)
{
Supplier = sup;
}
}
//View
<Window x:Class="APP.SDX.SupplierReports.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="483.8" Width="640" DataContext="{Binding SupplierReports}">
<Grid>
<DataGrid Name="G_lb_Selektion_Lieferanten"
Margin="0,26,0,27"
ItemsSource="{Binding Path=SupplierReports}"
SelectedItem="{Binding Path=MySelectedItem}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Lieferant" Binding="{Binding Supplier}" />
<DataGridTextColumn Header="Lieferant" Binding="{Binding Path=Supplier}" />
<DataGridTextColumn Header="Lieferant" Binding="{Binding Path=Supplier.Supplier}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
A DataGridTextColumn can only display the value of a scalar property of the type T in the IEnumerable<T> ItemsSource of the DataGrid.
If you want to display all S_Supplier objects inside the "Lieferant" column you could use a DataGridTemplateColumn with a nested DataGrid:
<DataGrid Name="G_lb_Selektion_Lieferanten"
Margin="0,26,0,27"
ItemsSource="{Binding Path=SupplierReports}"
SelectedItem="{Binding Path=MySelectedItem}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Lieferant" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Supplier}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
If there is only one S_Supplier object in the List<S_Supplier> and you want to display a property of this one you could bind to the first item in the list using an indexer:
<DataGridTextColumn Header="Lieferant" Binding="{Binding Supplier[0].Supplier }" />