Bind Unrelated property to DataGrid - wpf

EDIT: Solved
(I made another property in the ViewModel wrapper and bound to that)
I am trying to bind a property that is not related to the ObservableCollection that the DataGrid is bound to. The other columns are binding the way they should, it is just this one column that I can't seem to get to work.
I tried binding the property using RelativeSource AncestorType and directly to the DataContext with no luck.
The XAML, The ObservableCollection I am binding to obviously is called MonthlyRecords which is a collection of a different class and this is binding the way it should be. It is the property SelectedTenant.FullName which has nothing to do with the collection that is giving me grief.
<DataGrid ItemsSource="{Binding MonthlyRecords}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--Trying to bind this Property in the next line-->
<TextBlock Text="{Binding Path=SelectedTenant.FullName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="60" Header="Code" Binding="{Binding UpdateSourceTrigger=LostFocus, Path=TenantCode}" />
This is the class for the property I am trying to bind.
public class Tenant
{
public Tenant()
{
}
public int Code { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName => LastName + " " + FirstName;
public string Section { get; set; }
public Tenant(int code, string lastName = null, string firstName = null, string section = null)
{
Code = code;
LastName = lastName;
FirstName = firstName;
Section = section;
}
}
And this is the property in the ViewModel I am trying to bind to.
private Tenant _selectedTenant;
public Tenant SelectedTenant
{
get { return _selectedTenant; }
set
{
if (Equals(_selectedTenant, value)) return;
_selectedTenant = value;
OnPropertyChanged();
}
}
What else do I need to do to get this to bind to the DataGrid?

<DataGridTextColumn Header="Name" Binding="{Binding Path=SelectedTenant.FullName, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
Edit:
I have set AutoGenerateColumns="True"
<DataGrid ItemsSource="{Binding MonthlyRecords}" AutoGenerateColumns="True">
<DataGridTextColumn Header="Name" Binding="{Binding ElementName=ComboBoxTenant, Path=DisplayMemberPath}"/>

Related

WPF: Access SelectedItem of nested DataGrid

I have a nested DataGrid setup. How do I access the SelectedItem for the inner grid?
<DataGrid ItemsSource="{Binding OuterGrid}" RowDetailsVisibilityMode="Visible">
<DataGrid.Columns>
<DataGridCheckBoxColumn ..... />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding InnerGrid}" SelectedItem="{Binding SelectedInner}">
<DataGridTextColumn ..... />
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
SelectedInner is a property and even if I select a row in the InnerGrid in the UI, that property is still null.
How do I know which row was selected in the inner grid? I also use caliburn-micro.
It depends on where the 'SelectedInner' object is in your ViewModel. If the the object is in your ViewModel of your view (as is in the example below), you need to use RelativeResource in order to point out the datagrid where your bound object is.
class ShellViewModel:Screen
{
public BindableCollection<DataGridObject> OuterGrid { get; set; } = new BindableCollection<DataGridObject>();
public BindableCollection<DataGridObject> InnerGrid { get; set; } = new BindableCollection<DataGridObject>();
public DataGridObject selectedInner { get; set; } = new DataGridObject();
public ShellViewModel()
{
OuterGrid.Add(new DataGridObject {String1="water",String2="air",String3="earth" });
OuterGrid.Add(new DataGridObject {String1="water1",String2="air2",String3="earth2" });
OuterGrid.Add(new DataGridObject { String1 = "water3", String2 = "air3", String3 = "earth3" });
InnerGrid.Add(new DataGridObject {String1="water",String2="air",String3="earth" });
InnerGrid.Add(new DataGridObject {String1="water1",String2="air2",String3="earth2" });
InnerGrid.Add(new DataGridObject {String1="water3",String2="air3",String3="earth3" });
NotifyOfPropertyChange(() => OuterGrid);
NotifyOfPropertyChange(() => InnerGrid);
}
}
public class DataGridObject
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}
On the xaml you need to use RelativeResource
<DataGrid ItemsSource="{Binding OuterGrid}" RowDetailsVisibilityMode="Visible">
<!--<DataGrid.Columns>
<DataGridCheckBoxColumn />
</DataGrid.Columns>-->
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<DataGrid ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Path=DataContext.InnerGrid}" SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Path=DataContext.selectedInner}" >
</DataGrid>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Make sure you specify the correct ancestor which ma be window or usercontrol

WPF DataGridTextColumn direct edit

i have very simple DataGridTextColumn which should be modified on doubleclick event.
question is what should be added to avoid exception System.InvalidOperationException: ''EditItem' is not allowed for this view.'
<DataGrid x:Name="DG" ItemsSource="{Binding}" GridLinesVisibility="None" Grid.Column="3" Grid.Row="2">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding VariantSet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="60" />
</DataGrid.Columns>
</DataGrid>
simple class:
Public Class CName
Public Property Name As String = "not editable name"
End Class
on load simply added to datagrid
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Me.DG.Items.Add(New CName)
End Sub
when declared through template as following, there is no difference, same error
<DataGridTemplateColumn Header="Name" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
even when Implements ComponentModel.INotifyPropertyChanged is added to the CName, no difference
You've not shown us enough to tell what you're doing wrong.
Here's a working window with datagrid.
The code is c# but you can run it through a converter to vb if you particularly want vb. I think it's a bad idea for a beginner to choose vb nowadays. Almost nobody publishes samples using vb.
In my mainwindow:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding People}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding FirstName}" Header="First Name"/>
<DataGridTextColumn Binding="{Binding LastName}" Header="SurName"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
That viewmodel:
public class MainWindowViewModel : BaseViewModel
{
private ObservableCollection<Person> people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return people; }
set { people = value; }
}
public MainWindowViewModel()
{
People.Add(new Person { FirstName = "Chesney", LastName = "Brown" });
People.Add(new Person { FirstName = "Gary", LastName = "Windass" });
People.Add(new Person { FirstName = "Liz", LastName = "McDonald" });
People.Add(new Person { FirstName = "Carla", LastName = "Connor" });
}
}
Person just has those two properties first and last name:
public class Person : BaseViewModel
{
private string firstName;
public string FirstName
{
get { return firstName; }
set { firstName = value; RaisePropertyChanged(); }
}
private string lastName;
public string LastName
{
get { return lastName; }
set { lastName = value; RaisePropertyChanged(); }
}
That inherits from BaseViewmodel which just implements inotifypropertychanged.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here I am editing a row

WPF DataGrid binding don't work caliburn micro

I can't understand why it does not work..
This is my class
public class Article : Screen
{
public string Code { get; set; }
public string Description{ get; set; }
public decimal Cost{ get; set; }
public decimal Price{ get; set; }
}
This is XAML code of DataGrid:
<DataGrid Height="211" HorizontalAlignment="Left"
Margin="12,31,0,0" VerticalAlignment="Top" Width="521"
AutoGenerateColumns="False" ItemsSource="{Binding List}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Code}" Header="Code" />
<DataGridTextColumn Binding="{Binding Path=Description}" Header="Description" />
<DataGridTextColumn Binding="{Binding Path=Cost}" Header="Cost" />
<DataGridTextColumn Binding="{Binding Path=Price}" Header="Price" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" Height="39" HorizontalAlignment="Left"
Margin="223,262,0,0" VerticalAlignment="Top" Width="110"
x:Name="AllArticles"/>
And this is my viewmodel
[Export(typeof(IShell))]
public class ArtsViewModel : Screen
{
public List<Article> List = new List<Article>();
public void AllArticles()
{
Recover recover = new Recover(); //a model called Recover
List = recover.Import().Articles; //return a List of Article
NotifyOfPropertyChange("List");
}
}
WHY THE DATAGRID DON'T WORK ?
To enable the binding conventions in Caliburn.Micro, you normally use the x:Name property in your XAML. If you, instead of explicitly binding the List property to ItemsSource of your DataGrid, use the name convention like this:
<DataGrid x:Name="List" Height="211" HorizontalAlignment="Left"
Margin="12,31,0,0" VerticalAlignment="Top" Width="521"
AutoGenerateColumns="False">
I believe the subsequent bindings should work as desired.
Oh, and you also need to make List a property instead of a field:
public List<Article> List { get; private set; }
If you want to make sure that modifications to List are properly reflected in your data grid, you should also make your List property an IObservableCollection with a backing field:
private IObservableCollection<Article> _list;
public IObservableCollection<Article> List {
get { return _list; }
set {
_list = value;
NotifyOfPropertyChange(() => List);
}
}
a) Did you set the DataContext?
b) Try "Binding="{Binding Code}" (Without Path=), works fine for me.
For my UWP app I found that the x:Name convention didn't work and I need to use a bindable collection and bind directly to ItemsSource. I then used Path=PropertyName to specify the columns.
XAML:
<controls:DataGrid Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding RawCSVData}" AutoGenerateColumns="false">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn Header="Time" Binding="{Binding Path=Time}"/>
<controls:DataGridTextColumn Header="Signal" Binding="{Binding Path=Signal}"/>
<controls:DataGridTextColumn Header="Response" Binding="{Binding Path=Response}"/>
</controls:DataGrid.Columns>
</controls:DataGrid>
CSVData.cs:
public class CSVData: Screen
{
public double Time { get; set; }
public double Signal { get; set; }
public double Response { get; set; }
}
ViewModel.cs snippet:
public BindableCollection<CSVData> RawCSVData { get; set; }
private void _bookeditpage_ConfirmBook(Book _book, bool _isnewone)
{
if (_isnewone)
{
Books.Add(_book);
}
else
{
//doesn't refresh datagrid
SelectedBook = _book;
//
//works fine :)
SelectedBook.BookName = _book.BookName;
SelectedBook.IsPrenum=_book.IsPrenum;
SelectedBook.Publisher=_book.Publisher;
//
}
}

WPF datagrid dropdown two-way binding

I'm trying to get a dropdown column in a WPF datagrid (hosted in a user control derived from a base class called ControlBase) to bind properly. It initially populates fine from the object, and a populated dropdown appears when I edit the cell, but a selected value does not get back into the cell when I leave focus.
Here's my model and domain objects:
public class ModelBase : INotifyPropertyChanged
{
public IList<Person> Persons { get; set; }
}
public class UserControlModel : ModelBase
{
public ObservableCollection<DatagridRecord> SourceData { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DatagridRecord
{
public string Name { get; set; }
public Person ContactPerson { get; set; }
}
And in my xaml.cs, I set the DataContext via a Model property:
public UserControlModel _model;
public UserControlModel Model
{
set
{
_model = value;
DataContext = null;
DataContext = _model;
}
}
Here's my DataGrid column definition in xaml:
<DataGridTemplateColumn Header="Person" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ContactPerson.Name}"/></DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Persons,
RelativeSource={RelativeSource AncestorType={x:Type uch:ControlBase}}}"
DisplayMemberPath="Name"
SelectedValuePath="Id" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
I figure there is something wrong with tying the combobox selected value to the grid row, but I've gone around in circles trying to hook it up. Any advice would be appreciated.
Corey.
You're Missing a SelectedItem or SelectedValue binding:
<ComboBox ItemsSource="{Binding Path=DataContext.Persons,
RelativeSource={RelativeSource AncestorType={x:Type uch:ControlBase}}}"
DisplayMemberPath="Name"
--> SelectedItem="{Binding ContactPerson}" <--
SelectedValuePath="Id" />

DataGrid Silverlight Column Data binding

Let's say I have a Person Class like
Public class Person
{
public string firstName {set;get;}
public string lastName {set;get;}
}
in my ViewMode class I have an observableCollection of Person
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people= value;
FirePropertyChanged("People");
}
}
in my view I have a datagrid that ItemsSource is binded to People. I have a combobox in my view that its SelectedValue is binded to SelectedFieldValue and I want to add a row to my gridview with the value selected in this Combobox
private string _selectedFieldValue;
public string SelectedFieldValue
{
get { return _selectedFieldValue; }
set
{
_selectedFieldValue = value;
FirePropertyChanged("SelectedFieldValue");
People.Add(New Person{firstName = value});
}
}
the row is added to the Grid, but I don't see the value of the FirstName added. I tried several way to bind the column FirstName but couldn't get it working correctly.What's the correct way to bind my DataGridTextColumn in grid to firstName, lastName properties of the person class.
check out this..
<sdk:DataGrid x:Name="dataGrid4"
Height="160" ItemsSource="{Binding People}" AutoGenerateColumns="False" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn
Header="First Name"
Width="SizeToHeader"
Binding="{Binding firstName}"
FontSize="20" />
<sdk:DataGridTextColumn
Header="Last Name"
Width="SizeToCells"
Binding="{Binding lastName}"
FontSize="20" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>

Resources