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); }
}
Related
I am trying to bind a combobox to a dictionary.
"Phases" is a dictionary inside ProjectPlans class
public Dictionary<decimal?, string> Phases { get; set; }
Instead of displaying the values, combobox shows just word "(Collection)" and no dropdown arrow. What am I missing?
<c1:C1DataGrid ItemsSource="{Binding ProjectPlans}" IsReadOnly="True" RowDetailsVisibilityMode="VisibleWhenSelected">
<c1:C1DataGrid.Columns>
<c1:DataGridBoundColumn Header="ID" Binding="{Binding ProjectId}" />
<c1:DataGridComboBoxColumn Header="PHASE" Binding="{ Binding Phases}" DisplayMemberPath="Value" SelectedValuePath="Key"/>
I have found a workaround by using a DataTemplateColumn instead. But I still do not understand why it doesn't work with a DataComboBoxColumn. A bug?
I have a datagrid whose column headers I would like to change during runtime. I have tried something like this but doesn't work
<DatagridTextColumn Header="{Binding Path=MyNewHeader}" Binding=" {Binding Path=MyBindingProperty}" />
And In my MVVM
string myHeaderProperty;
public string MyHeaderProperty{
get{
return myHeaderProperty
}
set{
Set(ref myHeaderProperty, value);
}
But does not work. Any Ideas will be greatly appreciated
This should work provided that MyHeaderProperty belongs to the DataContext of the parent DataGrid, i.e. the view model:
<DataGridTextColumn Binding="{Binding MyBindingProperty}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.MyHeaderProperty, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
If you take a look in the output window you will see that you probably get a binding expression exception because MyNewHeader is not a property on you item in the row.
So you need to bind it to the parent via relativesource binding
Take a look at the following example
https://wpftutorial.net/BindingExpressions.html
I have a WPF/MVVMLight application. My main view/viewmodel has a data grid with code behind in the viewmodel. I recently decided I'd like
this datagrid in a 2nd view, one view is live data, one view is showing history of same data. I don't want to duplicate this code in two places so I decided to create a usercontrol with a viewmodel and move the appropriate datagrid/code to the usercontrol.
Here is the issue/question: I would like to set the usercontrol->datagrid->itemsource to a property in my main viewmodel. I've seen some examples where people create dependencyproperties but I'm not sure how to handle this because I can't inherit DependencyObject in my viewmodel because it already inherits ViewModelBase (from mvvm light), so I can't use GetValue/SetValue as seen below. I'm very new to this so I may be missing something very obvious. While looking for solutions to my problem I'm seeing that MVVM Light has some messaging functionality, would this be a better way to approach this? Is there a better approach to this than what i'm taking? Thanks for any guidance.
Example not pulled from my code, just used to show the GetValue/SetValue that I can't figure out how to use because I can't inherit DependencyObject.
public class MyViewModel : ViewModelBase
{
public static DependencyProperty MyProperty =
DependencyProperty.Register("GridItemSource", typeof(EventData), typeof(MyViewModel),
new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public EventData GridItemSource
{
get { return (EventData)GetValue(MyProperty); }
set { SetValue(MyProperty, value); }
}
--- Attemping to be more clear in what i'm hoping to achieve..
Below is an example of the datagrid in my usercontrol. My user control has a VM with code behind with properties for the bindings you see below and some additional code related to the context menu.
The issue i'm trying to resolve is how to set the usercontrol datagrid itemsource now that it is in a usercontrol that is nested in my main view, and eventually will be nested in one more view. My mainview has a ObservableCollection with the data I want the usercontrol/datagrid/itemsource set to. My thinking was I could create a dependency property in the usercontrol that my main view could use to set the source.
So in my main view where I add the user control i want to do something like this:
<Views:MyUserControl SomePropertyInUserControl="{Binding MyObservableCollection, Mode=TwoWay}"/>
In my user control view:
<DataGrid Grid.Row="0" SelectedItem="{Binding DataGridSelectedItem, Mode=TwoWay}" HeadersVisibility="All" HorizontalAlignment="Stretch" Name="lbMain" ItemsSource="{Binding MonitorEventItems, Mode=TwoWay}" AutoGenerateColumns="False" SelectionMode="Single" >
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding ContextMenuEventList,Mode=Default}"/>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Date" Binding="{Binding EventDateTime}" Width="Auto"/>
<DataGridTextColumn Header="Event Type" Binding="{Binding EventType}" Width="Auto"/>
<DataGridTextColumn Header="Folder/File" Binding="{Binding FFType}" Width="Auto"/>
<DataGridTextColumn Header="Name" Binding="{Binding EventFileFolderName}" Width="Auto"/>
<DataGridTextColumn Header="Full Path" Binding="{Binding EventFileFolderFullPath}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Do not use dependency properties in view models. Their main difference from ordinary properties is to be settable by bindings, styles, animations, etc., which you do not need in a view model.
Instead, create a property that raises the PropertyChanged event of the INotifyPropertyChanged interface, which is implemented by the ViewModelBase class. In addition, use ObservableCollection as property type, to notify about changes in the Items collection.
public class MyViewModel : ViewModelBase
{
private ObservableCollection<DataItem> items;
public ObservableCollection<DataItem> Items
{
get { return items; }
set
{
items = value;
RaisePropertyChanged("Items");
}
}
}
Same issue resolved here. (Note: does not use dependency properties) Example handling this with EventHandler and using MVVM Light Messaging. Other possible solutions/answers in thread as well.
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}" />
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>();