MultiBinding of grid visiblity in WPF - c#

I have a list view Item
and I want to collapse some of the items in the row of the list
I am using the grid for editing the data template.
I tried to work with multi-binding for the parameter
however I got that "DependencyProperty.UnsetValue"
would be happy for a code example.
if I am using 1 parameter all good.
can someone please explain to me how to use multi-binding
would be happy with a code example.
thanks.
Working code for 1 parmaeter I can use this code:
<ListView x:Name="LVGuiCoreBus" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" MouseDown="LVGui_MouseDown" Grid.Row="1" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding Source, Converter={StaticResource VisiblieGroupFilterBySourcecs}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="130"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DateNTimeStr}" Foreground="Green" Grid.Column="0" />
<TextBlock Text="{Binding Source}" Foreground="{Binding Source,Converter={StaticResource CoreBusPanelModuleColorConverter}}" Grid.Column="2" Background="{ Binding Source, Converter={StaticResource BackGroundFilterConverterBySource}}" />
<TextBlock Text="{Binding Destination}" Foreground="{Binding Destination,Converter={StaticResource CoreBusPanelModuleColorConverter}}" Grid.Column="4" Background="{ Binding Destination, Converter={StaticResource BackGroundFilterConvertorByDestantation}}" />
<TextBlock Text="{Binding Module}" Grid.Column="6" HorizontalAlignment="Center" Background="{ Binding Module, Converter={StaticResource BackGroundFilterByModule}}" />
<TextBlock Text="{Binding Controll}" Grid.Column="8" Background="{ Binding Controll, Converter={StaticResource BackGRoundFilterByControll}}" />
<TextBlock Text="{Binding Command}" Grid.Column="10" HorizontalAlignment="Center" Background="{ Binding Command, Converter={StaticResource BackGroundFilterByCommand}}" />
<TextBlock Text="{Binding HSCommand}" Grid.Column="12" HorizontalAlignment="Center" Background="{ Binding HSCommand, Converter={StaticResource BackGroundFilterByHsCommand}}" />
<TextBlock Text="{Binding Data_Str}" Grid.Column="14"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ContextMenu>
<ContextMenu x:Name="CMMenuCopy">
<MenuItem x:Name="MCICopyLine" Header="Copy Line" Click="MCICopyLine_Click" ></MenuItem>
<MenuItem x:Name="MCICopyText" Header="Copy Only Data Array" Click="MCICopyText_Click" ></MenuItem>
<MenuItem x:Name="MCIClear" Header="Clear" Click="MCIClear_Click"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
not working code multibinding:
<ListView x:Name="LVGuiCoreBus" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" MouseDown="LVGui_MouseDown" Grid.Row="1" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Visibility>
<MultiBinding Converter="{StaticResource MultiValueConvertorVisibility}" UpdateSourceTrigger="PropertyChanged">
<Binding ElementName="Source" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
<Binding ElementName="Destination" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
<Binding ElementName="Module" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
<Binding ElementName="Controll" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
<Binding ElementName="Command" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
<Binding ElementName="HSCommand" Path="Visibility" UpdateSourceTrigger="PropertyChanged"></Binding>
</MultiBinding>
</Grid.Visibility>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="130"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DateNTimeStr}" Foreground="Green" Grid.Column="0" />
<TextBlock Text="{Binding Source}" Foreground="{Binding Source,Converter={StaticResource CoreBusPanelModuleColorConverter}}" Grid.Column="2" Background="{ Binding Source, Converter={StaticResource BackGroundFilterConverterBySource}}" />
<TextBlock Text="{Binding Destination}" Foreground="{Binding Destination,Converter={StaticResource CoreBusPanelModuleColorConverter}}" Grid.Column="4" Background="{ Binding Destination, Converter={StaticResource BackGroundFilterConvertorByDestantation}}" />
<TextBlock Text="{Binding Module}" Grid.Column="6" HorizontalAlignment="Center" Background="{ Binding Module, Converter={StaticResource BackGroundFilterByModule}}" />
<TextBlock Text="{Binding Controll}" Grid.Column="8" Background="{ Binding Controll, Converter={StaticResource BackGRoundFilterByControll}}" />
<TextBlock Text="{Binding Command}" Grid.Column="10" HorizontalAlignment="Center" Background="{ Binding Command, Converter={StaticResource BackGroundFilterByCommand}}" />
<TextBlock Text="{Binding HSCommand}" Grid.Column="12" HorizontalAlignment="Center" Background="{ Binding HSCommand, Converter={StaticResource BackGroundFilterByHsCommand}}" />
<TextBlock Text="{Binding Data_Str}" Grid.Column="14"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ContextMenu>
<ContextMenu x:Name="CMMenuCopy">
<MenuItem x:Name="MCICopyLine" Header="Copy Line" Click="MCICopyLine_Click" ></MenuItem>
<MenuItem x:Name="MCICopyText" Header="Copy Only Data Array" Click="MCICopyText_Click" ></MenuItem>
<MenuItem x:Name="MCIClear" Header="Clear" Click="MCIClear_Click"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
public class MultiValueConvertorVisibility : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool x = System.Convert.ToBoolean(values[0]);
if (x)
{
return System.Windows.Visibility.Visible;
}
else
{
return System.Windows.Visibility.Collapsed;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

The most important piece conceptually that you are missing is the IMultiValueConverter, because visibility is just one value, at the end of the day, you want your grid to be either visible or not, so you have to tell the multibinding how to compose your multiple values into one.
Here is an example of how to get multi-binding to work:
The Grid:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:MultiValueConverter x:Key="MyCustomConvertor"/>
</Window.Resources>
<Grid ShowGridLines="True">
<Grid.Visibility>
<MultiBinding Converter="{StaticResource MyCustomConvertor}" UpdateSourceTrigger="PropertyChanged">
<Binding Path="Visibility"/>
</MultiBinding>
</Grid.Visibility>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
</Grid>
The Viewmodel that you bind to:
using System.ComponentModel;
using System.Windows;
namespace WpfApp1
{
class VM : INotifyPropertyChanged
{
#region WPF integration properties
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
private Visibility _visibility = Visibility.Hidden;
public Visibility Visibility
{
get { return _visibility; }
set
{
_visibility = value;
OnPropertyChanged(nameof(Visibility));
}
}
}
}
And finally the MultiValueConverter to convert multiple visibility bindings into one:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace WpfApp1
{
class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Contains(Visibility.Collapsed))
{
return Visibility.Collapsed;
}
if (values.Contains(Visibility.Hidden))
{
return Visibility.Hidden;
}
return Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Into the converter you could place any logic that you see fit, I just imposed a kind of hierarchy on the different visibility types.

Related

WPF Expander - Button in Header

I am building an expander control where I want to have an additional button in the header (additional to the toggle). When I add a button on the <Expander.Header>, the binded command is not triggered when it is clicked.
I have tried creating a style/template for the Expander and Expander.Header so that the Expander's toggle is only triggered on its click (rather than the entire header). I did this thinking that it is some sort of heirarchical issue where the header is consuming the click event rather than my button, but it did not work out as I had hoped...
Is there a specific way I should modify the template to allow for a button to be placed in the header and have the button's command executed?
xaml of my expander control - all bound to my VM, can confirm the binding works correctly:
<Expander Background="White">
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="center"
FontSize="16"
Margin="5 0 5 0"
Text="{Binding Name}"/>
<Button
Grid.Column="2"
Width="15"
Height="15"
Margin="10 10 10 10"
Command="{Binding OpenButtonCommand}"/>
</Grid>
</Expander.Header>
<TextBlock Margin="30 0 0 0">This is a test sub component</TextBlock>
</Expander>
Thank you
Actually it should be working;
Tested with following VM:
using Prism.Commands;
using Prism.Mvvm;
using System;
namespace WpfApp2
{
public class MainViewModel : BindableBase
{
public DelegateCommand OpenButtonCommand { get; private set; }
private string m_Name = "Test Name";
public string Name
{
get { return m_Name; }
set
{
m_Name = value;
RaisePropertyChanged(nameof(Name));
}
}
public MainViewModel()
{
OpenButtonCommand = new DelegateCommand(OpenButtonCommandExecute);
}
private void OpenButtonCommandExecute()
{
Name = "clicked";
}
}
}
and XAML:
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<Expander
Width="300"
HorizontalAlignment="Left"
Background="White">
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="1"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Name}"
TextAlignment="Left"
TextWrapping="Wrap" />
<Button
Grid.Column="2"
Width="15"
Height="15"
Margin="10,10,10,10"
Command="{Binding OpenButtonCommand}" />
</Grid>
</Expander.Header>
<TextBlock Margin="30,0,0,0">This is a test sub component</TextBlock>
</Expander>
</Grid>
Result:

UWP listview doesn't update after updating/adding an element to the MVVM collection list

I am frustrated with the listview ItemSource and MVVM binding. I am binding the listview with a data model. After loading the listview, I add another item to the listview, the update is not reflected in listview, but it shows that the number of items in the list collection is increased by one. If I add the add in the list collection in the constructor, it is shown in the listview.
XAML
<Page
x:Class="App20.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App20"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<local:VictimsController></local:VictimsController>
</Page.DataContext>
<Grid Padding="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="List of Victims/Trapped" HorizontalAlignment="Center" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
<Grid Grid.Row="1">
<ListView x:Name="ls" ItemsSource="{Binding VictimList, Mode=TwoWay}">
<ListView.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="0" Text="GPSLatitute" />
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="1" Text="GPSLongtitude" />
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="2" Text="Date" />
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Center" Grid.Column="0" Text="{Binding GPSLatitute}" />
<TextBlock HorizontalAlignment="Center" Grid.Column="1" Text="{Binding GPSLongtitude}" />
<TextBlock HorizontalAlignment="Center" Grid.Column="2" Text="{Binding Date}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
<Button Content="Publish" VerticalAlignment="Top"
Height="76" Width="114" Click="Publish_Click" />
</Grid>
Controller
public class VictimsController : INotifyPropertyChanged
{
List<VictimProfile> _victims = new List<VictimProfile>();
public VictimsController()
{
//Victims.Add(new VictimProfile() { GPSLatitute = 123, GPSLongtitude = 2333 });
}
public List<VictimProfile> VictimList
{
get
{
return _victims;
}
set
{
_victims = value;
OnPropertyChanged("VictimList");
}
}
public void addVictim(VictimProfile profile)
{
_victims.Add(profile);
OnPropertyChanged("VictimList");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Adding one item to the listview model or collection
async void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
string ReceivedMessage = Encoding.UTF8.GetString(e.Message);
//convert the message to json format
var payLoad = JsonConvert.DeserializeObject<VictimProfile>(ReceivedMessage);
// we need this construction because the receiving code in the library and the UI with textbox run on different threads
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
txt.Text += ReceivedMessage;
VictimsController model = this.DataContext as VictimsController;
model.addVictim(payLoad); //add item to the list collection
Debug.WriteLine("count: " + ls.Items.Count); //this shows that listview has one more item added to it, but nothing is shown in the listview
//ls.Items.Add(payLoad);
});
}
The point is the list collection has the new item and the ls (listview) also has the item, but it is not shown in the listview.
Finally I solved it.
Changing the list to observable collection has solved the problem.
ObservableCollection<VictimProfile> _victims = new ObservableCollection<VictimProfile>();

Databinding a user control within an itemscontrol

I have a UserControl that has a ViewModel containing an instance of my own custom class, call it Person. This ViewModel is set as the DataContext of the control. I am wanting to use this control in my main window which has, in its ViewModel a List of type Person called People. I am calling the control in my xaml like this:
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemsTemplate>
<DataTemplate>
<userControls:PersonDetails />
</DataTemplate>
</ItemsControl.ItemsTemplate>
</ItemsControl>
Properties within the control are bound in this manner.
<TextBlock Text="{Binding Person.Name}" />
When I run I am getting the correct number of Controls for the amount of People in the list but no details are populated. I know I am doing something simple wrong but can't find it. Please help.
EDIT:
My UserControl xaml looks like this:
<UserControl x:Class="AddressBook.UserControls.PersonDetails"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AddressBook.UserControls"
xmlns:vm="clr-namespace:AddressBook.ViewModels"
xmlns:enums="clr-namespace:AddressBook.Models"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="1000">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource PersonDetailHeader}" Text="Person Name:" />
<TextBlock Style="{StaticResource PersonDetailValue}" Grid.Column="1" Text="{Binding Person.Name,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource PersonDetailHeader}" VerticalAlignment="Center" Grid.Row="1" Text="Street Address:" />
<TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="1" Grid.Column="1"
Text="{Binding Person.StreetAddress, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="2" Text="Town:" />
<TextBlock Style="{StaticResource PeronDetailValue}" Grid.Row="2" Grid.Column="1"
Text="{Binding Person.Town, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="3" Text="County:" />
<TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="3" Grid.Column="1"
Text="{Binding Person.County, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="4" Text="Postcode:" />
<TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="4" Grid.Column="1"
Text="{Binding Person.Postcode, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Style="{StaticResource PersonDetailHeader}" Grid.Row="5" Text="Phone:" />
<TextBlock Style="{StaticResource PersonDetailValue}" Grid.Row="5" Grid.Column="1"
Text="{Binding Person.Phone, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Grid.Row="7" Grid.ColumnSpan="2" HorizontalAlignment="Center" Orientation="Horizontal">
<Button Style="{StaticResource ButtonStyle}" Content="Click" Command="{Binding ButtonClickCommand}" />
</StackPanel>
</Grid>
UserControl ViewModel:
public class PersonDetailsViewModel
{
public Person Person { get; set; }
public Command ButtonClickCommand { get; }
public PersonDetailsViewModel()
{
ButtonClickCommand = new Command(ButtonClick);
}
private void ButtonClick(object obj)
{
throw new NotImplementedException();
}
UserControl.xaml.cs:
public partial class PersonDetails : UserControl
{
public PersonDetails()
{
InitializeComponent();
}
}
Person.cs
public class Person : BaseModel
{
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged();
}
etc....
MainViewModel:
public class MainViewModel : BaseViewModel
{
public ObservableCollection<Person> People { get; }
= new ObservableCollection<Person>();
public MainViewModel()
{
PopulatePeople();
}
}
Now I understand why you were creating the PersonDetailsViewModel in the UserControl XAML, and what the problem was with the command.
The simplest way to fix this is to make MainViewModel.People a collection of PersonDetailsViewModel instead of Person. That's what I'd do.
But if you want to leave MainViewModel.People as it is, while still using PersonDetailsViewModel inside your UserControl, that makes sense to me. You could do this:
.xaml
<UserControl
x:Class="AddressBook.UserControls.PersonDetails"
DataContextChanged="PersonDetails_DataContextChanged"
...etc...
>
<!-- Give the Grid a name... -->
<Grid x:Name="OuterGrid">
.xaml.cs
public PersonDetails()
{
InitializeComponent();
}
private void PersonDetails_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// ...so we can give the Grid a different DataContext
OuterGrid.DataContext = new PersonDetailsViewModel {
Person = (Person)this.DataContext
};
}
Now it will inherit a Person for its DataContext, but internally it will use a PersonDetailsViewModel based on that Person.
Not sure if that's what you are trying to do.
If you only wish to display some carac of every Person present in your People List you could do :
<Grid>
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" />
<TextBlock Grid.Column="1" Text="{Binding Age}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
That will bind you Text on the Name property of each element of your list, so on the name of each Person of your list.
As your ItemsSource is already bound to People, the Binding of your Items is already on each Person of your List, so you should call your properties directly as {Binding Name} and not {Binding Person.Name}

How to bind buttons in ListView DataTemplate to Commands in ViewModel? (MVVMLight)

I have a list view with following code:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="allToDoItemsListBox"
ItemsSource="{Binding AllToDoItems,Mode=OneWay}"
Margin="12,0,12,0"
Width="440"
ItemTemplate="{StaticResource ToDoListBoxItemTemplate}" />
</Grid>
The datatemplate is as follows :
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="ToDoListBoxItemTemplate">
<Grid HorizontalAlignment="Stretch" Width="420">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<CheckBox
IsChecked="{Binding IsComplete, Mode=TwoWay}"
Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock
Text="{Binding ItemName}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Foreground="Gray"
Grid.Column="1" Grid.ColumnSpan="2"
VerticalAlignment="Top" Margin="-36, 12, 0, 0"/>
<Button
Grid.Column="3"
x:Name="deleteTaskButton"
BorderThickness="0"
Margin="0, -18, 0, 0"
Command="{Binding Path=DeleteCommand}" CommandParameter="{Binding}"/>
<Image
Source="/Images/appbar.delete.rest.png"
Height="75"
Width="75"/>
</Grid>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
I tried to bind the Button Command "DeleteCommand" to a ICommand in ViewModel but it failed what should i do?
The Code in ViewModel is :
public ICommand DeleteCommand { get; private set; }
In the ViewModel Construct :
DeleteCommand = new RelayCommand<object>(Delete);
And The delete method :
private void Delete(object obj)
{
ToDoItem newToDoItem = obj as ToDoItem;
DeleteToDoItem(newToDoItem);
}
I need to pass the item to DeleteToDoItem() method as parameter when the corresponding delete button of each item in the list is pressed but the command does not fire here what should i do?
In your ViewModel you want to pass a ToDoItem to your Delete command.
DeleteCommand = new RelayCommand<ToDoItem>(DeleteToDoItem);
And your DataTemplate's DataContext is different from the ListView's.
<Button Command="{Binding ElementName=allToDoItemsListBox,
Path=DataContext.DeleteCommand}"
CommandParameter="{Binding}" />
That should do it.

WPF Combobox w/ Observable Collection in Composite Collection

I'm trying to populate a combobox with an observable collection and a header row using a composite collection. The header is there but I can't get the OC objects to populate. I'm just starting to use OC's so might be something basic I'm missing.
Customer class:
public class Customer : ViewModelBaseMain
{
public string CustomerName { get; set; }
public int CustomerId { get; set; }
}
I populate the OC, which is a dependency property, from a datatable (I verified that the OC is being populated correctly):
ObservableCollection<Customer> dpCustomerListOC = new ObservableCollection<Customer>();
foreach (DataRow drow in dpHeaderCustTable.Rows)
{
Customer Customer = new Customer();
Customer.CustomerId = Convert.ToInt32(drow["customer_id"]);
Customer.CustomerName = drow["customer_name"].ToString();
dpCustomerListOC.Add(Customer);
}
In the usercontrol resources, I specify the VM as a static resource:
<local:ReservationViewModel x:Key="ReserveVM"/>
I've tried two different ways from posts I've read.
First way w/ collection in the resources using x:reference:
<ComboBox x:Name="cboCustHeader" Grid.IsSharedSizeScope="True" IsEditable="False"
ItemsSource="{DynamicResource items}">
<ComboBox.Resources>
<CompositeCollection x:Key="items">
<ComboBoxItem IsEnabled="False">
<Border Style="{StaticResource ComboHeaderBorder}">
<Grid Style="{StaticResource ComboHeaderStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="ID"/>
<TextBlock Grid.Column="2" Text="Name"/>
</Grid.Children>
</Grid>
</Border>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={x:Reference cboCustHeader}, Path=DataContext.dpCustomerListOC}"/>
</CompositeCollection>
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Text="{Binding Path=CustomerId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="{Binding Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Second way specifying the source:
<ComboBox x:Name="cboCustHeader" Grid.IsSharedSizeScope="True">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="False">
<Border Style="{StaticResource ComboHeaderBorder}">
<Grid Style="{StaticResource ComboHeaderStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="ID"/>
<TextBlock Grid.Column="2" Text="Name"/>
</Grid.Children>
</Grid>
</Border>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Path=dpCustomerListOC, Source={StaticResource ReserveVM}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding Path=CustomerId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Column="2" Text="{Binding Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
Add a public property exposing the collection on your view model:
public ObservableCollection<Customer> DpCustomerListOC
{
get { return dpCustomerListOC; }
}
And bind the combo's ItemsSource to it:
<ComboBox x:Name="cboCustHeader" Grid.IsSharedSizeScope="True">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="False">
<Border Style="{StaticResource ComboHeaderBorder}">
<Grid Style="{StaticResource ComboHeaderStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="ID"/>
<TextBlock Grid.Column="2" Text="Name"/>
</Grid.Children>
</Grid>
</Border>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Path=DpCustomerListOC, Source={StaticResource ReserveVM}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="7"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding Path=CustomerId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Column="2" Text="{Binding Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Make sure the collection is populated in the constructor.
It seems like you are missing to NotifyPropertyChange. When working with ComboBoxs I usually just do something like this:
<ComboBox ItemsSource="{Binding Customers, Mode=OneWay}"
SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"> </ComboBox>
Then in your class:
public type Customers
{
get{return _Customers}
set{_SelectedCustomer = value;
NotifyPropertyChanged("Customers");}
}
public type SelectedCustomer
{
get{return _SelectedCustomer;}
set{_SelectedCustomer = value;
NotifyPropertyChanged("SelectedCustomer")}
}
The syntax may be off a little bit but something like this should populate the ComboBox.

Categories

Resources