I've got a DataGrid whose main purpose is to allow the user to enter data in its cells.
However, the first field in this grid should display the contents of a list.
How would I go about setting the data source for just one column?
EDIT: I want to achieve something like this:
in addition to my comment, one easy way would be to wrap your itemssource collection and add a list for every item - with the data you expect.
the good thing is you do not have to set a different source for your first column, you just have to bind to the new list property
EDIT:
i hope i got your problem.
lets say you have a itemssource for your grid:
List<MyObject> _list;
your myobject contains Asc/Desc, GroupBy, Having, Dispplayorder properties.
so i would create a MyObjectWrapper and add the Field property
public class MyObjectWrapper
{
public MyObject WrappedOject {get;set;}
public string Fields {get;set}
}
you end up with a new
List<MyObjectWrapper> _wrapperlist;
these collection have all information you need to display.
Specify your own DataGridTemplateColumn in DataGrid.Columns, and set the DataGridTemplateColumn.CellTemplate to whatever it is you're looking to place in that column.
For example, if you wanted it to be a ComboBox pointing to some other list of fields:
<DataGrid.Columns>
<DataGridTemplateColumn Header="Fields">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ElementName=MyWindow, Path=DataContext.SomeCategoryList}"
SelectedItem="{Binding SomePropertyOnDataItemInRow}" />
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
I'm using a property called SomeCategoryList that's on MyWindow.DataContext, however you can set the binding to point to wherever your field list is stored
Related
I have 2 DataGridComboBoxColumn in my datagrid ClassificationComboBox & DisclosureNoteComboBox.
The first one works ok, I see it populated with data.
What i want is that when an item is selected in the first one i.e. ClassificationComboBox , I want the second one to display a list of data from the selected Item in the first one.
The selected Item in ClassificationComboBox is of type Classification and this has a list called ClassificationRecords which I want populated in the DisclosureNoteComboBox.
This is a snippet of my XML.
<materialDesign:DataGridComboBoxColumn Header="Classification" IsEditable="False" x:Name="ClassificationComboBox"
ItemsSourceBinding="{Binding ElementName=TrialBalanceViewName, Path=Report.Classifications}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValueBinding="{Binding ClassificationRecord.ClassificationId}"
/>
<materialDesign:DataGridComboBoxColumn Header="Disclosure Note" IsEditable="False" x:Name="DisclosureNoteComboBox"
ItemsSourceBinding="{Binding ElementName=ClassificationComboBox, Path=SelectedValueBinding.ClassificationRecords}"
SelectedValuePath="DisclosureNote"
SelectedValueBinding="{Binding ClassificationRecord.DisclosureNote}"
/>
I suspect my problem is how to construct the ItemsSourceBinding for the second one (especially the path). I think this is wrong but I am unsure how to call the selected Item of the first one and call ClassificationRecords on it to be the ItemSource of the second one
ItemsSourceBinding="{Binding ElementName=ClassificationComboBox, Path=SelectedValueBinding.ClassificationRecords}"
You can't approach the problem like this because data grid columns definitions do not contain the data you want. They are instructions to the DataGrid on how to display its items, and not containers of the items themselves.
To solve this you need to bind to the data model, not the column definitions.
Column A: binds to property foo.
Column B: binds to property bar
with an ItemsSource populated by property foo.
I have an ObservableCollection<double> that is defined in my ViewModel.
ListWidthsFlat=new ObservableCollection<double>();
ListWidthsFlat.Add(120);
ListWidthsFlat.Add(200);
My XAML code :
<DataGrid ItemsSource="{Binding Path=ListWidthsFlat}" AutoGenerateColumns="False"
CanUserAddRows="True" CanUserDeleteRows="True" HorizontalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static p:Resources.Width}" Binding="{Binding ., UpdateSourceTrigger=PropertyChanged}" Width="100" />
</DataGrid.Columns>
</DataGrid>
What I want is show the ObservableCollection, then offer possibility to add/delete items from my ObservableCollection<double>.
When I do the same thing on an ObservableCollection<T> all is working perfectly.
But when binding to ObservableCollection<double>, seems that parameters CanUserAddRows is not working.
Edit :
After additional tests, it seems the problem is that when I bind a DataGrid to an ObservableCollection<T>, and set CanUserAddRow=True, an additional empty line is automatically created (so I can edit it and add a new item to ObservableCollection.
When I bind a DataGrid to ObservableCollection<double>, no empty line is created.
Here is a screenshot to make it more understandable :
To properly do a CanUserAddRows the object list being bound to must implement IEditableCollectionView Interface which provide basic editing capabilities to the collection being bound to. Within that the item being presented from the list has to have a public default parameterless constructor.
Because a value type double does not have a constructor the grid detects that and does not provide an add row; hence you see the failure on double alone, but it works on the object, (class) instances of List<T> which have double as a specific property.
To work around the limitation,
Create a class which has a public parameterless constructor and one double property.
Then create your list of class and bind to that ObservableCollection (or List works too actually if you don't need the overhead of the observablecollection.) with a set of your values.
In Xaml set the column in the datagrid to point to the double property.
You may need to write a value constructor which will take in a float and return a string, and convert a string to a float.
I have a datagrid where i use a foreach(DataGridRow gvr in myDataGrid) and I need to be able to take the info from specific cells in the row and put them into their respective class properties i.e.(a.MessageName = gvr.column["MessageName"].value.ToString()). But I haven't figured out how to get at the info based on the column. Here's what i have so far...
foreach (DataGridRow gvr in dgAnnouncement.Items)
{
Announcement a = new Announcement();
a.MessageName = gvr.Column["MessageName"].Value.ToString();
a.Message = gvr.Column["Message"].Value.ToString();
}
And here's my XAML...
<DataGrid AutoGenerateColumns="False" Height="631" ItemsSource="{Binding}" HorizontalAlignment="Left" SelectionMode="Single" SelectionUnit="FullRow" Margin="12,124,0,0" Name="dgAnnouncement" VerticalAlignment="Top" Width="976" >
<DataGrid.Columns>
<DataGridTextColumn Header="MessageName"></DataGridTextColumn>
<DataGridTextColumn Header="Message"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
After searching the web I still havent found a solution that works for me so thanks in advance for any help.
Ok so it sounds like you want to have a bound list of items that the user can add to and fill in values on
If you want to do this, the best way is via binding on your columns:
<DataGrid AutoGenerateColumns="False" Height="631" ItemsSource="{Binding}" HorizontalAlignment="Left" SelectionMode="Single" SelectionUnit="FullRow" Margin="12,124,0,0" Name="dgAnnouncement" VerticalAlignment="Top" Width="976" >
<DataGrid.Columns>
<DataGridTextColumn Header="MessageName" Binding="{Binding MessageName, Mode=TwoWay}"></DataGridTextColumn>
<DataGridTextColumn Header="Message" Binding="{Binding Message, Mode=TwoWay}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
The question is - what do you have ItemsSource set to at the moment?
Ideally this should be a strongly typed collection of Appointment objects (ObservableCollection<Appointment> maybe). Do you want the users to be able to add new rows? If so you need to either provide a button which performs the Add on the source collection or let the datagrid do it (I think it supports an 'empty row' where you can type values... though I usually use Telerik's RadGridView). Generally when you have a grid that has an empty row for the user to add a new value, the grid will look at the underlying collection that's bound and call the appropriate method to add an item. If this collection doesn't support an Add method (such as IBindingList does) I think the default is to create a new item using the parameterless constructor for the type (not too sure on this, might be worth doing some reading)
Basically, by binding these properties TwoWay it means that each item in the list can be edited by the user directly in the grid. If the user changes a property, that property on the underlying object will be affected*. This means you don't need to write any code to wire this all up. Binding can also be done from control->control so for instance you can bind another grids ItemsSource to the SelectedItems property on your first grid, and it will automatically update with which items you have selected.
Check out my answer here for more info on DataBinding
How does data binding work?
Edit:
I might add that any changes to the underlying object without going through the grid will still show up in the grid but only if the object implements a property changed notification mechanism e.g. INotifyPropertyChanged (in System.ComponentModel namespace)
I have a View that is linked to my ViewModel using a DataTemplate, like this
<DataTemplate DataType="{x:Type ViewModels:ViewModel}">
<Views:View />
</DataTemplate>
The ViewModel holds a property ProcessOption that is of type MyEnum?, where MyEnum is a custom enumeration that has let's say 3 values: Single, Multiple and All. I am trying to bind a combobox to this property, so the approach I am following is:
ViewModel has a property of List<string> that is
public List<string> Options
{
get
{
return Enum.GetNames(typeof(MyEnum)).ToList();
}
}
to which I bind the ItemsSource property of the Combobox. Then, in addition to the ProcessOption property, the ViewModel also has an OptionName property (of string), which is intended to hold the selected option's name. The ViewModel implements INotifyPropertyChanged and both properties raise the PropertyChanged event in their setters. The binding I am using then is:
<ComboBox ItemsSource="{Binding Options}"
SelectedItem="{Binding OptionName}"
SelectedValue="{Binding ProcessOption}"/>
This works fine up to this point. Initially the combobox is empty and both properties are null, and when the user selects an option this is propagated to the ViewModel as it should.
The problem appears when I load the data from a Database, and I want to load the controls with initial values. In this case, in the ViewModel's constructor I have this:
this.ProcessOption = objectFromDB.ProcessOption // this is the value restored from DB, let's say it is MyEnum.Multiple
this.OptionName = Options.First(x => x.Equals(Enum.GetName(typeof(MyEnum), objectFromDB.ProcessOption)));
The problem is, although the above sets the two properties to their correct values, they are later set to null from the Combobox binding, so the initial values are not kept. I have also tried to do something like if (value == null) { return; } in the properties' setters, in which case they have the correct values after the View loads, however the Combobox still does not display the correct option, it is empty.
I should also note that I've also tried setting IsSynchronisedWithCurrentItem and it doesn't make any difference, apart from the fact that the first element is displayed instead of the combobox being empty.
Can anyone help with this binding? Any help will be very much appreciated, this is driving me crazy!
<ComboBox ItemsSource="{Binding Options}"
SelectedItem="{Binding OptionName}"
SelectedValue="{Binding ProcessOption}"/>
Your binding doesn't look like it should work at all -- you don't have TwoWay binding set up, and I think SelectedItem and SelectedValue is an either/or proposition.
I suggest that you get rid of OptionName and just bind SelectedItem to ProcessOption (TwoWay) with an IValueConverter that will convert to/from string.
I have a datagrid from wpf Toolkit, with the itemsource binded to a Observable<Item>. In the Item Class, I have another Observable<bool> list containing the values to be displayed.
I want to display these values in a custom template. If possible, I want to show other rows as well (which are normal Properties).
How can I perform this? Thank you for your answers.
Update (just to make clear): the second list should be displayed in normal columns, not as master/detail. Imagine the second list would contain 2 bools, and the Item class contains 1 extra property. In that case, 3 columns should be shown.
You can create second datagrid and bind SelectedItem.Items from first grid to itemssource of second. Or you can include second datagrid in row details of your datagrid like this:
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Items}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
Take a look at this examples and this
You can write attached property to datagrid which will create additional columns for you on grid. This property implementor will define binding with individual Observable values.