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?
Related
I want to add a DatagridCheckBoxColumn that is not in the bound source, so I can select specific rows via gui. Then I want to iterate over the datagrid and call a stored procedure with each selected row.
<DataGrid
x:Name="G_DG_Data"
Grid.Row="0"
Grid.RowSpan="1"
Grid.Column="0"
Grid.ColumnSpan="3"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
ClipboardCopyMode="IncludeHeader"
ItemsSource="{Binding MyGridData}">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Mark" />
<DataGridTextColumn Binding="{Binding Data}" Header="Data" />
</DataGrid.Columns>
</DataGrid>
I tried to iterate over Datagrid.Items and Datagrid.Itemssource but I am unable to access the CheckBoxColumn values / the CheckBoxColumn.
Is this approach possible? Is there a better way?
EDIT:
I have this Property not in the ViewModel, because the ViewModel is EntityFramework with Database first and I fill the Datagrid directly from the Database, where the Property is useless.
private ObservableCollection<SDH_CRModul_Sniffer> myGridData;
public ObservableCollection<SDH_CRModul_Sniffer> MyGridData
{
get { return myGridData; }
set
{
if (myGridData == value) return;
myGridData = value;
OnPropertyChanged("MyGridData");
}
}
Where SDH_CRModul_Sniffer is the Entity created from Entity Framework.
The items in Items have no idea about any CheckBox in the UI. That's why you should bind it to a property of an item:
<DataGridCheckBoxColumn Binding="{Binding YourProperty}" Header="Mark" />
If you don't, the state of a CheckBox may get lost or corrupted as you scroll through the rows because of the UI virtualization that is enabled by default.
The only way to get the value of the CheckBox if you don't bind is to iterate through the visual elements in the UI, and this won't work if some of them are virtualized away.
I have this Property not in the ViewModel, because the ViewModel is EntityFramework with Database first and I fill the Datagrid directly from the Database, where the Property is useless.
Entity Framework generates partial classes so you could then create another partial class with the same name and add the property to this one. Or wrap the entity in a view model class and use this one in the DataGrid instead of the entity class.
I am new to WPF and data binding. I an trying bind a list of strings to a row on a data grid column, Animals, in WPF. I'm not sure what I am doing wrong, but the column shows up as blank when I run the solution. To add rows this way instead of using a DataTable and looping through the list and adding the rows because the next column I am adding will be of type Combobox so I didn't wanna implement Ui elements into the DataTable.
public List<string> Animals {get; set;} = new List<string>
{
"Dogs",
"Cats",
"Birds"
};
<DataGrid
filtergrid:DataGridExtensions.UseBackgroundWorkerForFiltering="True"
filtergrid:DataGridExtensions.IsFilterVisible="True"
filtergrid:DataGridExtensions.IsClearButtonVisible="False"
ColumnHeaderStyle="{StaticResource {ComponentResourceKey
TypeInTargetAssembly={x:Type filtergrid:DataGridHeaderFilterControl},
ResourceId=DataGridHeaderFilterControlStyle}}"
AutoGenerateColumns="False"
ItemsSource="{Binding}"
CanUserAddRows="False"
HorizontalAlignment="Stretch"
Margin="10,50,0,0"
HorizontalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Header="Animals" Width="Auto"
Binding="{Binding Animals}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
Your code seems right but in order to show something I think you need to do a selection.
So you could bind a property to SelectedIndex which is set to 0 or whatever you like.
I have two DataGrid elements. The first one is loaded with article data and the second one should display further data based on the SelectedItem of the first DataGrid.
The binding of the first DataGrid with the article data works perfectly.
Now I have a second one, which should display more data depending on the selection of the first one. In the ViewModel.cs I have created the following:
public BindableCollection<ArticleDataModel> ArticleDataList {
get; set;
}
public BindableCollection<QuantityModel> WarehouseQuantityList {
get; set;
}
private ArticleDataModel currentSelection;
public ArticleDataModel CurrentSelection {
get {
return currentSelection;
}
set {
currentSelection = value;
NotifyOfPropertyChange(() => currentSelection);
this.LoadQuantity();
}
}
The first DatagridView is bound to ArticleDataList, all data is displayed correctly. When selecting a dataset I can see in the debugger that the method LoadQuantity() is called and that WarehouseQuantityList contains all data.
However, the data is not displayed to me, not even record rows are displayed.
The binding in XAML looks like this:
<!-- Artikel ListView -->
<DataGrid Grid.Column="0" Grid.Row="0"
x:Name="ArticleDataList"
AutoGenerateColumns="False"
AlternatingRowBackground="#fafafa"
FontSize="14"
RowBackground="#eee"
HorizontalGridLinesBrush="#ddd"
VerticalGridLinesBrush="#ddd"
RowHeaderWidth="0"
SelectionMode="Single"
SelectionUnit="FullRow"
SelectedItem="{Binding CurrentSelection}">
and
<DataGrid x:Name="WarehouseQuantityList"
AutoGenerateColumns="False"
AlternatingRowBackground="#fafafa"
FontSize="14"
RowBackground="#eee"
HorizontalGridLinesBrush="#ddd"
VerticalGridLinesBrush="#ddd"
RowHeaderWidth="0"
SelectionMode="Single"
SelectionUnit="FullRow">
Binding is both times over x:Name, the items are bound like this:
<DataGridTextColumn Header="Lagerplatz" Width="250" Binding="{Binding Path=Lagerplatz}"/>
The binding is technically the same for both, but I don't get any errors or exceptions. The loaded data is correct and also available in WarehouseQuantityList. Only the DataGrid does not see them. So I assume that I'm making a mistake, just don't know where?
I am always open for hints :-)
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); }
}
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>();