I have a dataGrid that binding the itemSource. This itemSource has a collection of elements (related entities for a weak relationship)that for design has only one element.
I would like to show in the dataGrid the properties of this related entities, so I I am trying this binding:
<DataGridTextColumn Header="IDRelatedEntity" Binding="{Binding RelatedEntities.ElementAt(0).IRelatedEntity, Mode=TwoWay}" />
But this show nothing.
I know that I could use a converter for this, but I would like to avoid the use of a converter for this, I guess that it would be a simple way to do that.
Thanks.
You're almost there. Use this:
<DataGridTextColumn Header="IDRelatedEntity" Binding="{Binding RelatedEntities[0].IRelatedEntity, Mode=TwoWay}" />
Related
I've been a long time lurker on SO and have only recently decided to set up an account. I've been spending quite a lot of hours trying to solve this problem I've been having without asking but I here it is.
What I'm trying to accomplish:
I have a list of strings, e.g: Mango, Banana, Melon (let's call it fruits) and I want to display it as a table in XAML (WPF), where it appears as row values on the left side, and the right side will be combo boxes that will allow users to pick a value. I'm currently stuck on the displaying part of this problem. I decided to use a DataGrid to display the list, and the code behind will do the data bindings.
Here's the XAML part of it:
<DataGrid x:Name="FruitDataGrid" Height="265" VerticalAlignment="Center" Margin="-7,8,-2,-6" HorizontalAlignment="Left" Width="1188" AutoGenerateColumns="False">
<!-- If AutoGenerateColumns was true only the length is displayed.-->
<DataGrid.Columns>
<DataGridTextColumn x:Name="fruitsDisplay" Header="Fruits" MinWidth="450" IsReadOnly="True" />
<DataGridComboBoxColumn Header="Number of Boxes" MinWidth ="200" CanUserResize="True" Width="*"></DataGridComboBoxColumn>
</DataGrid.Columns>
Code Behind so far has been really simple, and after many attempts this is the most recent one.
private void populateFruitList()
{
FruitDataGrid.DataContext = fruitDataTable;
//Binds a datatable instance to the datagrid, does not display anything.
}
What I've been attempting:
Turn the List of Strings into an Observable Collection, or Enumerable, and do FruitDataGrid.itemsource = fruitsObservable;
The above method works, except that it will only display the length of each string value (if autogeneratecolumns is true), which is not what I want. If autogeneratecolumns was false then I can see the rows being shown, but not the string values.
I also tried using DataView or DataGridView but I failed to define it in the XAML, VS2012 says that there isn't such a thing.
I've been trying to do data binding as well as the MSDN Article says, but VS2012 never manages to bind to the list properly.
I then attempted to change my list into a datatable and [use a datagridview as specified here but again the XAML tells me it is not a valid class][2]. I am also aware of the performance impact datagridview might have but at this point I just want to display a table really.
I've also seen that some tutorials use a DataGrid.datasource method but that isn't in the DataGrid I want, I think it's a different class? (the DataGrid I am attempting to use is System.Windows.Controls.DataGrid, and the other one is in Windows Forms [As shown here][3])
Thanks again for taking the time to look into this.
EDIT
Trying to bind to the list in XAML this way:
<DataGrid x:Name="FruitDataGrid" Height="265" VerticalAlignment="Center" Margin="-7,8,-2,-6" HorizontalAlignment="Left" Width="1188" AutoGenerateColumns="False" ItemsSource="fruitDataList">
I get the error in XAML "The type converter for IEnumerable does not support converting from a string" which I think is because I'm doing it wrong. The table now shows a lot of empty rows though.
Trying ItemSource="{Binding fruitDataList}" (where fruitDataList is a List) yields a blank table, and an error in VS for BindingExpression path error.
To sum up what the previous issue was, thanks to Muds, and hours of trying, the Binding in the XAML was not properly done.
In FruitDataGrid, this property should be written as ItemSource="{Binding}" this tells the XAML code to bind to whatever object the DataContext is assigned to in the code behind.
After that, within the DataGrid.Column, this property is needed.
Binding="{Binding Path=.}"
It had to be exactly that for me. lack of the dot or not enclosing it in quotes will not display the fruits.
Thus, for clarity:
In the XAML:
<DataGrid x:Name="FruitDataGrid"
Height="265" VerticalAlignment="Center" Margin="-7,8,-2,-6"
HorizontalAlignment="Left" Width="1188"
AutoGenerateColumns="False"
ItemSource="{Binding}">
<!-- If AutoGenerateColumns was true only the length is displayed.-->
<DataGrid.Columns>
<DataGridTextColumn
x:Name="fruitsDisplay"
Header="Fruits" MinWidth="450"
IsReadOnly="True"
Binding="{Binding Path=.}"/> <!--Exactly like this -->
<DataGridComboBoxColumn
Header="Number of Boxes"
MinWidth ="200"
CanUserResize="True" Width="*" />
</DataGrid.Columns>
And then in the codebehind (filename.xaml.xs)
//Anywhere you plan to bind the list in my case FruitList
List<string> fruitList = new List<string>();
fruitList.add("Melon");
fruitList.add("Mango");
fruitList.add("Banana");
FruitDataGrid.DataContext = fruitList;
And now you'll have a very nice list displayed as a table. What killed 2 days of my time was the binding path should have a . (dot) right there, and I was binding to a string instead of an object (you literally bind to a string "fruitList" when you do Binding = {"fruitList"}. This amateur mistake stems from me insufficiently self-learning XAML.
Thanks again, Muds. I'll select yours as the answer because it helped me, and it feels weird accepting my own answer.
Considering your binding is set to your viewmodel correctly.. do this
ItemsSource="{Binding fruitDataList}"
then
<DataGrid.Columns>
<DataGridTextColumn
x:Name="fruitsDisplay"
Binding="{Binding Path=FRUITS_PROPERTY_NAME_IN_COLLECTION}"
Header="Fruits"
MinWidth="450"
IsReadOnly="True" />
<DataGridComboBoxColumn
ItemsSource="{Binding TO_List_of_Numbers}"
Header="Number of Boxes"
MinWidth ="200"
CanUserResize="True" Width="*"></DataGridComboBoxColumn>
Context
The WPF datagrid allows developers to manually bind each column to a property of the ItemsSource, like this:
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"/>
<DataGridTextColumn Binding="{Binding Age}"/>
<DataGridTextColumn Binding="{Binding Country}"/>
</DataGrid.Columns>
</DataGrid>
The advantage of these column bindings is that Visual Studio will display design-time warnings if the bindings don't correspond to a property of the objects in the People collection. It will also provide a list of valid properties through intellisense.
I'm developing a different type of grid and am having trouble getting this functionality. Currently I have developed something like this:
<my:Grid ItemsSource="{Binding People}">
<my:Column Property="Name" />
<my:Column Property="Age" />
<my:Column Property="Country" />
</my:Grid>
Internally these string based properties are looked up through type.GetProperty() at runtime.
My Question
How can I implement property binding like in the DataGrid example? I have looked through the decompiled source of DataGrid and found that DataGridBoundColumn.Binding is of type BindingBase.
I reappropriated BindingBase into my custom grid but I'm still not getting strong typing or intellisense options, probably because BindingBase has no idea what type it applies to, but I don't see how to provide it with the context it needs.
Change your implemantion of the property my:Column.Property to a Dependency Propertie. That will allow you to use Bindings, maybe that helps. But the intellisense around Binding is more a deal of the VS-XAML-designer and not your code.
//http://msdn.microsoft.com/en-us/library/ms752914(v=vs.110).aspx
public static readonly DependencyProperty PropertyProperty =
DependencyProperty.Register(
"Property", typeof(Boolean),
);
public bool IsSpinning
{
get { return (bool)GetValue(PropertyProperty ); }
set { SetValue(PropertyProperty , value); }
}
I would like to bind a DataGrid to an object, and not to a collection of objects,
My scenario is,
I have a single record with a few columns in the database that the user will keep updating,
and for UI design purposes we would like the user to update it through a DataGrid with a single row. As a work around, I created an ObserverbaleCollection and added that record,
but I would like to do it the right way so please if someone could guide me.
Thanks
p.s.
as requested Below
here his the xaml for the datagrid i tried
<DataGrid CanUserAddRows="False" IsReadOnly="False" AutoGenerateColumns="False" ItemsSource="{Binding FacilityDefaults}">
<DataGrid.Columns>
<DataGridTextColumn Header="Price " Binding="{Binding Price,UpdateSourceTrigger=LostFocus}"/>
<DataGridTextColumn Header="Default Temperature" Binding="{Binding DefaultTemperature,UpdateSourceTrigger=LostFocus}"/>
</DataGrid.Columns>
</DataGrid>`
Using a Datagrid as an edit mechanism for a single record seems like the wrong choice in the first place. A Datagrid is made to work with a collection of items not really a single item. But if you insist on using it in this manner, then binding to a collection with just your one record in it is the simplest way to make it work. A DataGrid's ItemsSource expects an IEnumerable (MSDN), so binding it to a single object would be awkward to say the least.
I have a WPF C# datagrid that is read only, its contents are loaded from an external XML file and other forms manipulate the XML file by adding, editing and removing data.
I would like the datagrid to reload when a change is made, however there doesn't appear to be an easy way of doing this.
I intend on putting a 'refresh' of some sort when the 'editing' form closes.
I have tried datagrid.items.refresh() without success among a few other bits of failed code. (learning C#/WPF)
How am I supposed to do this?
XAML:
<Grid.DataContext>
<XmlDataProvider Source="E:\downloader\downloadConfig.xml" XPath="/download/downloadItem"></XmlDataProvider>
</Grid.DataContext>
<DataGrid x:Name="downloadList" Height="191" VerticalAlignment="Top" ItemsSource="{Binding}" AutoGenerateColumns="False" AlternatingRowBackground="Gainsboro" IsReadOnly="True" DataContext="{Binding}" IsSynchronizedWithCurrentItem="True">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding XPath=ID}" Width="50"></DataGridTextColumn>
<DataGridTextColumn Header="Name" Binding="{Binding XPath=Name}" Width="350"></DataGridTextColumn>
<DataGridTextColumn Header="Status" Binding="{Binding XPath=Status}" Width="100"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Bind the DataGrid to a collection that implements the INotifyCollectionChanged interface. Objects that implement this interface will raise events when their contents change, and DataGrid will listen for these events and update itself accordingly.
There is a built-in generic class, ObservableCollection, which takes care of all of this for you. It's usually easiest to just use it. It does have one gotcha, though, which is that it can only be modified from the main thread. If you need to modify it from another thread, use Dispatcher.Invoke (or BeginInvoke) to avoid getting exceptions.
Note that these only notify of 'row-level' changes - the addition, removal, replacement of entire objects from the collection. To have the DataGrid also update itself when objects within the collection change, implement INotifyPropertyChanged on them.
I realize this means a bunch of extra coding since you'll need to implement classes to go between the XML and the collection, but it is the preferred option. On the upside, it should perform better. The DataGrid will be able to only update the rows it needs to update, rather than completely redrawing itself (which can be an expensive operation in WPF).
Getting the current object with BindingContext is no problem but what do I do when I have selected many rows in the datagridview? How do I iterate through each object?
This wasn't easy or fun. Binding multiple selected rows in the datagrid isn't supported by default. I use MultiSelectBehavior from Functional Fun:
http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html
These are my notes to get it to work:
To get this to work, I did this:
Add this namespace definition to the view:
xmlns:ff="clr-namespace:FunctionalFun.UI.Behaviours;assembly=MultiSelectBehavior"
Within the datagrid, add the last two lines shown here (ff:... and SelectionMode....):
ff:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedTasks}"
SelectionMode="Extended"
Note: In the view model, SelectedTasks cannot be null, even when first declared.
No:private ObservableCollection selectedTasks;
Yes: private ObservableCollection selectedTasks = new ObservableCollection();
And this is some actual code that works:
xmlns:ff="clr-namespace:FunctionalFun.UI.Behaviours;assembly=MultiSelectBehavior"
<DataGrid Grid.Row="1" AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column"
ItemsSource="{Binding SelectedApplicationServer.ApplicationsWithOverrideGroup}"
ff:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedApplicationsWithOverrideGroup}"
SelectionMode="Extended">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Application.Name}" Header="Name" Width="150" />
<DataGridTextColumn Binding="{Binding Path=Application.Version}" Header="Version" Width="100"/>
<DataGridTextColumn Binding="{Binding Path=CustomVariableGroup.Name}" Header="Override Group" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Hope it helps.
Edit: I simply added the Functional Fun code as a project within my solution, and then I referenced it within my view project:
Assuming your DataGridView is bound to a BindingSource, using the DataMember property this way :
myDataGridView.DataSource = someBindingSource;
myDataGridView.DataMember = "SomeCollectionProperty";
Then you can retrieve the list of bound items behind your DataGridView:
IList dataBoundItems =
((CurrencyManager)grid.BindingContext[grid.DataSource, grid.DataMember]).List;
You may also want to cast this list to an IEnumerable<T> using :
var myItems = dataBoundItems.OfType<myClass>();