I'm trying to create a custom header for a ListView / GridView where the user can easily filter items using Text inputs:
I've created DependencyProperties on the UserControl's code-behind and are binding the Xaml datacontext to itself (name=root). The binding i'm having trouble with is the TextBox inside the Header - it simply doesn't work. If i set a 'ModelVariantId' to "Whatever" it doesn't show and likewize i don't get any PropertyChanged events when writing in it..
I've found -some- similar sounding questions on the internet but nothing exactly matching.
<ListView ItemsSource="{Binding ElementName=root, Path=CombinedCarModels}">
<ListView.View>
<GridView>
<GridViewColumn Width="120" DisplayMemberBinding="{Binding ModelVariantId}">
<GridViewColumn.Header>
<GridViewColumnHeader HorizontalContentAlignment="Stretch">
<StackPanel>
<Label>ModelVariantId</Label>
<TextBox Text="{Binding ElementName=root,
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</StackPanel>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
If you define a HeaderTemplate, this binding should work:
Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=CarModelFilter.ModelVariantId, Mode=TwoWay}"
XAML:
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" HorizontalAlignment="Stretch">ModelVariantId</Label>
<TextBox Grid.Row="1" HorizontalAlignment="Stretch"
Text="{Binding ElementName=root, Path=CarModelFilter.ModelVariantId, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
There is no element named "root" in the same naming scope as the header, but you should be able to bind to a parent element in the visual tree using the RelativeSource property.
This assumes that the parent UserControl has a CarModelFilter property.
Related
I am trying to bind combo box inside of a list view.
Here is the Xaml:
<UserControl
<UserControl.Resources>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="IsSelected" Value="true" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<DockPanel MinWidth="724" Height="387" DataContext="{Binding DeviceDiagnosticsMainVeiwModel}">
<globals:SaveNotificationPopup IsOpen="{Binding IsSaveNotificationPopupOpen}" SaveCommand="{Binding SaveCommand}" CancelCommand="{Binding CloseSaveNotificationPopup}" Placement="Relative" VerticalOffset="300" HorizontalOffset="200" />
<StackPanel>
<Grid Width="250" HorizontalAlignment="Left" Margin="10,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="23" />
<RowDefinition Height="Auto" MinHeight="23" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{x:Static ml:MultiLang._2001}" x:Name="ML_0209" />
<Label Grid.Row="1" Content="{x:Static ml:MultiLang._2009}" x:Name="ML_0211" />
<ComboBox Grid.Column="1" Grid.Row="0" ItemsSource="{Binding Manufacturers}" DisplayMemberPath="WorkingPlace" SelectedValuePath="PersonCode" SelectedValue="{Binding Path=ManufacturerID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Devices}" DisplayMemberPath="Model" SelectedValue="{Binding Path=SelectedDevice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<ListView DockPanel.Dock="Top" Margin="10,25,10,10" Width="460" ScrollViewer.HorizontalScrollBarVisibility="Hidden" HorizontalAlignment="Left" ItemsSource="{Binding CurrentDeviceDiagnostics}" SelectedItem="{Binding SelectedDeviceDiagnostics}" BorderThickness="1">
<ListView.View>
<GridView>
<GridViewColumn Header="Command" Width="180" x:Name="ML_0007">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DiagnosticsCommand, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" FontSize="12" Margin="0 5 0 0" Width="165" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" Width="180" x:Name="ML_0010">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DiagnosticsName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" FontSize="12" Margin="0 5 0 0" Width="165" TextAlignment="Center" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Units" Width="100" x:Name="ML_0013">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Units}" DisplayMemberPath="UnitName" SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="85" Margin="0,1,0,0" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ContentControl DockPanel.Dock="Top" Content="{Binding AddNewCAView}" />
<!-- No Diagnodtics message-->
<globals:NoItemsMessage DockPanel.Dock="Top" Visibility="{Binding NoDiagnosticsVisibility}" HorizontalAlignment="Left" Margin="10" Width="360" Height="40" Message="{x:Static ml:MultiLang._742}" x:Name="ML_0017" />
<!-- delete popup -->
<globals:DeletePopup IsOpen="{Binding IsConfirmDeletePopupOpen}" Message1="{x:Static ml:MultiLang._746}" DeleteCommand="{Binding DeleteCommand}" CancelCommand="{Binding CancelDeleteCommand}" Placement="Center" VerticalOffset="-200" HorizontalOffset="200" x:Name="ML_0018" />
</StackPanel>
</DockPanel>
</UserControl>
The Dock Panel binds to : DataContext="{Binding DeviceDiagnosticsMainVeiwModel}"
The list view binds are :
ItemsSource="{Binding CurrentDeviceDiagnostics}" SelectedItem="{Binding SelectedDeviceDiagnostics}"
and both CurrentDeviceDiagnostics and SelectedDeviceDiagnostics are members of DeviceDiagnosticsMainVeiwModel.
One text box (for example) in the list view bind like this :
Text="{Binding DiagnosticsCommand, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
CurrentDeviceDiagnostics is an observble collection of DeviceDiagnosticsVeiwModel which contains DiagnosticsCommand.
And works just fine (both of the textboxes).
The checkbox binds like this:
ItemsSource="{Binding Units}" DisplayMemberPath="UnitName" SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And Units and DiagnosticsUnit are also members of DeviceDiagnosticsVeiwModel.
The Item source works fine and populated the way i need.
The problem is with the selected item... its initialized as empty although DiagnosticsUnit has an integer value.
Funny thing is that if I choose something from the combobox, the code goes to the DiagnosticsUnit property to the set function, so I'm not sure abou t this problem...
Even if I will change the combobox to a textbox using DiagnosticsUnit , it will work !
I used a lots of mvvm comboboxes and didnt have any binding problems but its the first time i have a combocox inside of a list view...
Please help me.
Thank you all !!!
Let's break this down... you said:
both CurrentDeviceDiagnostics and SelectedDeviceDiagnostics are members of DeviceDiagnosticsMainViewModel.
That's as it should be. Then you said:
CurrentDeviceDiagnostics is an ObservableCollection<DeviceDiagnosticsViewModel>
Therefore the data bindings from inside the CellTemplates should point to properties from the DeviceDiagnosticsViewModel class as you correctly pointed out.
Therefore, looking at your ComboBox code below, I can only think of two possible reasons why this wouldn't work:
<ComboBox ItemsSource="{Binding Units}" DisplayMemberPath="UnitName"
SelectedValuePath="UnitCode" SelectedValue="{Binding DiagnosticsUnit, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="85" Margin="0,1,0,0" />
One suggestion would be to use the ComboBox.SelectedItem property instead and to data bind to an instance of the same type as your Units collection... however, if that is just a collection of ints, then this step wouldn't help. The only other thing would be to ensure that you have correctly implemented the INotifyPropertyChanged interface in the DeviceDiagnosticsViewModel class.
O.K
I finally found the problem !
It wasn't a binding problem.
this is very silly.
i had this code
public void GetItems()
{
_devicesDiagnostics = new ObservableCollection<DeviceDiagnosticsVeiwModel>(
(from dd in _dal.GetItems()
orderby dd.DeviceID
select new DeviceDiagnosticsVeiwModel(dd)
{
ObjectStatus = Status.NoChange,
Units = _manager.UnitRepository.Units
}
) .ToObservableCollection<DeviceDiagnosticsVeiwModel>()
);
}
The Units is a member in the view model (DeviceDiagnosticsVeiwModel) and it's actually the comboboxs item source.
Inside of DeviceDiagnosticsVeiwModel there's an int property which is the selected item. It didnt load it right.
The correct code will be
public void GetItems()
{
_devicesDiagnostics = new ObservableCollection<DeviceDiagnosticsVeiwModel>(
(from dd in _dal.GetItems()
orderby dd.DeviceID
select new DeviceDiagnosticsVeiwModel(dd)
{
ObjectStatus = Status.NoChange,
Units = new ObservableCollection<UnitViewModel>(this.Units),
}
)
.ToObservableCollection<DeviceDiagnosticsVeiwModel>()
);
}
I had to give every object a new list. It worked.
Hi I am having issues with understanding WPF databinding with nested objects.
I have a workgroup class containing a List of User_activation objects called ListMembers and I would like to display its properties. How do I access its nested properties? This class contains another object called User that has its username and ultimately I would like to display the username in the combobox instead of WPF_test.User_activation.
Below is the XAML code and corresponding layout:
<ListView x:Name="ListViewWorkgroups" VerticalAlignment="Top" Height="Auto" Width="Auto" ItemsSource="{Binding listWorkgroups}">
<ListView.View>
<GridView>
<GridViewColumn Width="auto" Header="Workgroup" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
<GridViewColumn Width="auto" Header="Skills">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ListSkills}" ></ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="auto" Header="Members">
<GridViewColumn.CellTemplate>
<DataTemplate >
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ListMembers}" ></ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Layout: http://i50.tinypic.com/ydy5h.png
Thank you!
You need to set the ItemTemplate for the ComboBox
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ListMembers}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding User.Username}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
As an alternative, if you don't need anything complex you can bind the DisplayMemberPath
<ComboBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ListMembers}" DisplayMemberPath="{Binding User.Username}"/>
You use the "." to access properties like you would in normal c# code
This is just a follow-up to the previous answer. I just discovered that in Bindings, you may use a leading period in a filesystem fashion:
<ComboBox ItemsSource="{Binding .ListMembers}">
<DataTemplate>
<TextBlock Text="{Binding .User.Username}"/>
</DataTemplate>
That syntax adds nothing semantically, but in some cases makes the statement more readable (and XAML can certaintly use that!)
Here's a better example:
<ComboBox ItemsSource="{Binding Caliber}" DisplayMemberPath=".Thickness" />
where Thickness is a property of Caliber.
We built our user interface from XML definitions (not XAML) but underneath we use a WPF to present the UI. That is at runtime, we create the WPF UI based on our XML definition.
<ListView ItemsSource="{Binding}" Width="400px" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Name="ListView"
ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Grid.ColumnSpan="3"
SelectionChanged="ListView_SelectionChanged" BorderThickness="0" IsTabStop="False">
<ListView.View>
<GridView x:Name="grid">
<GridViewColumn Width="100px">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ModuleName}" Width="100px" Foreground="Black" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="200px">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=ModuleUserCount, TargetNullValue=''}" MaxLength="6" Name="txtModuleUserCount" KeyDown="txtModuleUserCount_KeyDown" MinWidth="180" MaxWidth="200" BorderBrush="Gray"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</ListView.View>
</ListView>
I generate 4 module through XML SO UI have four TextBox in these dynamic TextBox tabIndex is not working. I tried KeyboardNavigation.TabNavigation="Cycle" but it's not working. How to make tab navigation work for this layout?
I know its too too late, but I would like to answer. I had the same problem. I tried following way and it resolved my problem: Set:
KeyboardNavigation.TabIndex="10"
or whatever value you require in ListView itself.
I have the following ItemsControl in Silverlight 3.
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn" Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding ElementName=MyBtn, Path=Tag}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Problem is that I have the ItemsControl bound to the Collection in my ViewModel, but I need the button to trigger a command on the ViewModel which is of course not Available in the DataContext of the button since it only contains the collection.
I can make the command fire by setting my ViewModel as a Resource and then binding to it as a StaticResource, but I want to know why the element to element binding won't work in this scenario. I would prefer not to use the StaticResource binding because that requires the default constructor on the ViewModel and so I can't inject my data easily.
UPDATE
I'm working through this slowly... Looking at the suggestions from Peter I realized that I may have more serious binding issues because of my page setup. I have two possible road blocks, but first things first.
My Items control above is wrapped in another items control that is bound to an observable collection. I moved my items control so that its a direct child of the root items control. It was wrapped in another control that I'll get to. So I tried the element binding to the items control ControlItemList, but its a collection so it can't find my ButtonCommand method in that Collection. What I need to do is bind to an item within that collection. How do I bind to a single item within the collection?
<Grid x:Name="LayoutRoot" Background="White"><!-- DataContext="{Binding Path=., Source={StaticResource lvvm}}">-->
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="ControlItemList" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ControlName" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center" />
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
So, Assuming I can get the above to work the other road block is that my items control is wrapped in another usercontrol that I'm using to get DataTemplateSelector type functionality. I think this control may be blocking me from getting to the parent DataContext. Is there a limit as to how far up the tree you can go?
<common:DeviceControlTemplateSelector Grid.Column="1" FieldType="{Binding ValueType}" Margin="0,2,0,2">
<common:DeviceControlTemplateSelector.StringTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.StringTemplate>
<common:DeviceControlTemplateSelector.DateTimeTemplate>
<DataTemplate>
<TextBox Text="this is date time binding" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.DateTimeTemplate>
<common:DeviceControlTemplateSelector.BooleanTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value, Mode=TwoWay}" />
</DataTemplate>
</common:DeviceControlTemplateSelector.BooleanTemplate>
<common:DeviceControlTemplateSelector.IntegerTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ValueCollection}" DisplayMemberPath="Value" SelectedIndex="{Binding Value, Mode=TwoWay}" >
</ComboBox>
</DataTemplate>
</common:DeviceControlTemplateSelector.IntegerTemplate>
<common:DeviceControlTemplateSelector.MultiButtonTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=DataContext.ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</common:DeviceControlTemplateSelector.MultiButtonTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks again everyone for your help!
the best solution i have found so far to deal with this situation is by Dan Wahl where he uses a DataContextProxy.
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Try to change
from
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
to
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=DataContext.ButtonCommand}"
OR
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot.DataContext, Path=ButtonCommand}"
In MVVM messaging is a strong concept for cummunication between ViewModels. You could use PRISM Eventaggregator or the Messenger class of MVVM Light Toolkit. On the button command you can publish a message and subscribe to it in the viewmodel.
I want to have a ComboBox in the header cells of my GridView (currently inside of a ListView) populated with the data from that column. To be used a bit like the 'auto filter' feature in Excel.
The problem I have is I can't get to the data for the column from inside the header cell. At the moment, I have something like this:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="headerTemplate">
<StackPanel>
<ComboBox></ComboBox>
<TextBlock FontSize="16" Text="{Binding}" Grid.Row="1" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListView Width="400" Height="200">
<ListView.View>
<GridView ColumnHeaderTemplate="{StaticResource headerTemplate}">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Namespace" DisplayMemberBinding="{Binding Namespace}"/>
</GridView>
</ListView.View>
<x:Type TypeName="Visual"/>
<x:Type TypeName="UIElement"/>
<x:Type TypeName="FrameworkElement"/>
</ListView>
</Grid>
But I'm not sure how to get the data to populate the combo boxes in the header. What's the best way of doing this?
A quick and dirty way would be something like this:
<Grid.Resources>
<DataTemplate x:Key="headerTemplate">
<StackPanel>
<ComboBox ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}" DisplayMemberPath="{Binding}"/>
<TextBlock FontSize="16" Text="{Binding}" Grid.Row="1" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
If you really need a filter-like list, you'll have to figure out how to get distinct values only.