I have a DataGrid which shows a list of type Product. What I want to do is to have a sort of master/detail view where the grid shows the master data, and then a collection of text boxes (etc) shows the detail view. The detail is mainly for large text columns where it is not appropriate to show them in the grid due to the size of the text. There are also some data items that are shown in both the grid and the detail area.
Additionally, I need it so that both the grid and the detail area are bound together, so that a change in either causes the underlying data source to be updated - and if (e.g.) the grid is changed those changes are immediately reflected in the detail area and vice versa.
I think that this can be achieved with binding and dependency properties - but how would I set this up?
Note: as the user can control what columns are displayed on the grid the bindings are defined manually in code..
var column = new DataGridTextColumn()
{
Header = attribute.Name,
Binding = new Binding(attribute.ColumnName) { TargetNullValue = string.Empty },
IsReadOnly = attribute.IsReadOnly
};
dgProductsList.Columns.Add(column);
If you have named the DataGrid all you need to do is bind the DataContext of your details area to the SelectedItem of the grid, all bindings inside the area then will be to the item's properties e.g.
<DataGrid Name="dg" .../>
<Border DataContext="{Binding SelectedItem, ElementName=dg}">
<StackPanel>
<TextBox Text="{Binding SomeTextProperty, UpdateSourceTrigger=PropertyChanged}"/>
<!-- ... -->
</StackPanel>
</Border>
This is two way by default, add UpdateSourceTrigger=PropertyChanged to make the property update immediately, otherwise the text is changed when the focus leaves the TextBox.
Related
I have a ComboBox that shows empty space below its values. See picture below.
The data in the view model is set in a button click handler. When I set the values in the initialization of the view model the ComboBox is fine. When I try to create a small example the ComboBox also has the expected size. It seems it depends somehow on the context where I set the values in the view model but I cannot figure out. I hope someone can give me a hint.
Code in the view model
Repositories.Clear();
Repositories.Add("One");
Repositories.Add("Two");
Repositories.Add("Three");
SelectedRepository = "One";
Code in XMAL
<ComboBox MinWidth="150"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
IsEnabled="{Binding CT.Connected}"
ItemsSource="{Binding CT.Repositories,
UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding CT.SelectedRepository}"/>
The ItemsSource you are binding to needs to be an ObservableCollection. The ComboBox will display the initial blank space if you bind the ItemsSource to any enumerable type that doesn't raise property changed when its items change.
When I try to create a small example the ComboBox has the expected size.
By that logic it maybe not the combobox, but the data which is in the list. Can you verify that there are not 8 items where the textual value to display is empty for the last four items or so?
Or
Maybe a style is causing the extra space. Try removing the style from the combobox such as this
<ComboBox Style="{x:Null}"/>
and see if it has any effect to the visual result.
Or
Also how about not setting the data and see if the drop down has the same size?
I have a view model that has two properties. One of them is myDataGridSelectedItems, that is update in the selection changed event of the datagrid (I am using MVVM light to convert an event to command).
The second property is myText, that is the text that has a textbox in the view.
In my view I have a textBox which text depends on the selection of the dataGrid, if the selection is one item then I put the information of a column of the dataGrid in the textBox, if the selection is 0 or more than 1, then I clear the textBox.
To do that, I use the following code:
<TextBox Height="23" HorizontalAlignment="Stretch" Margin="5,26,0,0" Name="mytextBox" VerticalAlignment="Top"
Text="{Binding ElementName=Principal, Path=DataContext.MyDatagridSelectedItems, Converter={StaticResource TextBoxValueConverter}}">
This works fine because when I select one row in the data grid the textBox has text (the text that the convert returns) and is empty when I select more that one or unselect all rows.
However, in this way the property myText is not update because I don't set the binding, because the binding of the Text property in the axml use the converter, not the property myText of the view model.
So I was wondering if it possible to set two bindings in the Text property of the textBox, or if exists some way to update the myText property in the view model when the text in the TextBox changes.
Thanks.
You are doing it the wrong way around:
Right now, you have view logic encoded in a converter in the view. But view logic is precisly what the view model is there for.
You should have a property for the text of that text box in the view model and bind the text box only to that property.
In the view model you change its value according to the selection.
I have a tab control with a few tabs. When a tab is selected, I set the content of the tab to its corresponding view model.
I also have a DataTemplate defined for the base view model that all of the other view models derive from:
<DataTemplate DataType="{x:Type vm:BaseViewModel}">
<view:BaseView/>
</DataTemplate>
This way, my view models, which are nearly identical, will be displayed using the same base view.
BaseView is a user control. In BaseView I have an Infragistics XamDataGrid defined. It seems that only one instance of this grid is created for all of the view models, meaning I can switch between tabs as many times as I want but the user control is never recreated from scratch.
How does WPF handle the lifetime of user controls when combined with DataTemplates?
The problem I am trying to solve is that in the xaml of BaseView, I have defined a Field in the XamDataGrid like so:
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:FieldLayout.FieldSettings>
<igDP:FieldSettings DataValueChangedNotificationsActive="true"
AllowCellVirtualization="False"
AllowResize="True"
AllowRecordFiltering="True"/>
</igDP:FieldLayout.FieldSettings>
<igDP:Field Name="IsDirty" Visibility="Collapsed"/>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
The IsDirty column (all of the view models have an IsDirty property) is only correctly collapsed the first time the grid is displayed. When I click another tab, the grid's data source changes, a new FieldLayout is created by the grid, and it doesn't pick up the Collapsed setting for IsDirty. As a result the IsDirty column is visible. My thinking was if I can force the user control to be totally recreated, I could avoid this issue.
Add DataTemplate to Resources and set x:Shared="false"
I have question on the checkbox.
First of all,
I have a usercontrol which has a list box like this and this user control will be switched by 2 button and then the data source is changed and then the the displayed officer status will be changed:
When I check the checkbox, Officers[0].IsOnDuty will be changed to true.
The problem is:
When I click another button and switch to another data source, this checked check box is still checked but the Officers[0].IsOnDuty for this data source is false.
How to solve this?
The data context of the list box item is an item for your officers collection, not the collection itself. And using a one way binding is incorrect, as the data source (the officer) will not be updated. So change the DataTemplate to:
<CheckBox IsChecked="{Binding Path=IsOnDuty, Mode=TwoWay}" />
*Here is the list box xaml:
<ListBox ItemsSource="{Binding OfficersCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=Officers[0].IsOnDuty, Mode=OneWay}" />
*
The problem with your approach is that once you change the ItemsSource (by switching to the next page) your chekcbox is still bound to the item of the first collection. I think this happens because you explicitly use an indexer for the binding Path=Officers[0].IsOnDuty
Your samplelist box xaml does not really make sense. the ItemsSoruce is a OfficerCollection and your ItemTemplate binds to a collection of Officers too. Depending on what you are trying to accomplish you should do one of the following:
If your are just interested in the first officer (as your sample suggest), add a DependencyProperty FirstOfficer (or a INotifyPropertyChanged) property to your collection and bind to it: IsChecked="{Binding Path=Officers.FirstOfficer, Mode=OneWay}"
If you however are interested in all Officers and want checkboxes for all of them you should create a DataTemplate for the Officer type and use this as the ItemTemplate.
Generally you can stay out of a lot of trouble if you stick with MVVM and really tailor your ViewModel objects very close to what the View needs so you can bind your View to the ViewModel in the simplest possible way. Think of the ViewModel as the View you want to build but without a visual representation.
When creating a custom column design for a Silverlight DataGrid, is there any way to bind or make use of the DataGrid's SelectedItem property?
I wish to display a static element, but have it only visible for the row that is selected.
A simple example of what I'm after:
<data:DataGrid>
<data:DataGrid.Columns>
...
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="Selected" Visibility="{IsSelected ? Visible : Collapsed}">
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
...
</data:DataGrid.Columns>
</data:DataGrid>
The column does not need to contain any other elements or bindings.
It does not need to control the Visibility property specifically - any means that hides the element on all rows except the selected row will do.
Can this be done with styles? (Note that there is already one style applied to the DataGrid).
Does RowDetails meet your needs?
Ultimately I made do with a work around - the class used as the ItemSource had a Selected property added which was automatically updated to sync with changes to the list. Then I added a Visiblity property (I could have used a converter as well) to converted the boolean selected to a visibility value, which was used to control the visual appearance of controls in a column of the selected item in the list.