Custom Data Binding to more than one thing WPF C# - wpf

I have a XAML file which has two things,
a Combo Box
a stackpanel
How to I make it such that a change in the combo box will automatically get the stackpanel to switch from one stack panel to another stackpanel.
My comboBox is something like
<ComboBox x:Name="MCbConnect" SelectedIndex="{Binding EnConnectionType}" Loaded="m_cbConnect_Loaded" SelectionChanged="m_cbConnect_SelectionChanged" Width="100"></ComboBox>
where EnConnectionType is a property like this
private ConnectionType _enConnectionType;
public ConnectionType EnConnectionType
{
get { return _enConnectionType; }
set { SetProperty(ref _enConnectionType, value, "EnConnectionType"); }
}
And ConnectionType is
public enum ConnectionType { Rs232 = 0, Can = 1, Ethernet = 2 };
So i have implemented INotifyChanged interface here already. But I dont know how to bind this data with a stackpanel container which will allow me to switch to a different stackpanel view automatically in the background.
Example of a XAML that i would like to switch to is
<GroupBox x:Class="Gui.CtrlCommSocketSettings"
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:src="clr-namespace:Akribis.Gui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Header="Comm Settings"
mc:Ignorable="d"
d:DesignHeight="80" d:DesignWidth="300">
<Grid Height="70" VerticalAlignment="Top">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,3,0"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Width" Value="120"/>
<Setter Property="Margin" Value="0,1"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,4"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Server:" Name="MTextBlockServer"/>
<TextBlock Grid.Row="1" Text="Port:"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Server}" Name="MTextBoxServer"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Port}"/>
</Grid>
with cs file with something like
namespace Gui
{
public partial class CtrlCommSocketSettings
{
public CtrlCommSocketSettings()
{
InitializeComponent();
}
}
}
I dont want to do this programatically as I know i want to avoid coupling between the model and the view together.
example of what i dont want to do but have at the moment:
in the main XAML, I have an empty stackpanel
<StackPanel Orientation="Vertical" Name="MCtrlCommSettings"></StackPanel>
and i am explicitly adding to this stackpanel by doing something like
MCtrlCommSettings.Children.Clear();
MCtrlCommSettings.Children.Add(_serverCtrlCommSettings);
How do i go about doing this automatically? Like how InotifyChanges will update between views and model automatically. Any suggestion will be gladly welcomed.
Reading online, it seems i need to implement some kind of an observable list

I'd suggest to go with DataTemplates and separate ViewModels for each connection type. Just specify DataTemplates with target type of each ViewModel, and after that use ContentControl, with content property binded CurrentConnection property of your main view model, which would depend on the SelectedValue of the ConnectionType combobox.
UPDATE
Source code to illustrate the solution:
XAML
<Window x:Class="MVVMExample.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:MVVMExample"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="425">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Rs232ConnectionViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Rs232Port:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Rs232Port}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:CanConnectionViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="CanParam:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding CanParam}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:EthernetConnectionViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Server:" />
<TextBlock Grid.Row="1" Text="Port:"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding EthernetServer}" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding EthernetPort}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox x:Name="MCbConnect" SelectedValue="{Binding CurrentConnectionType}" ItemsSource="{Binding ConnectionTypes}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ConnectionType}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ContentControl Grid.Row="1" Content="{Binding CurrentConnectionType}" />
</Grid>
</Window>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
ObservableCollection<ConnectionTypeViewModel> _connectionTypes;
public ObservableCollection<ConnectionTypeViewModel> ConnectionTypes
{
get { return _connectionTypes; }
private set { _connectionTypes = value; }
}
public MainWindowViewModel()
{
ConnectionTypes = new ObservableCollection<ConnectionTypeViewModel>(new ConnectionTypeViewModel[]
{
new Rs232ConnectionViewModel() { ConnectionType = ConnectionType.Rs232, Rs232Port="COM1"},
new CanConnectionViewModel() { ConnectionType = ConnectionType.Can},
new EthernetConnectionViewModel() { ConnectionType = ConnectionType.Ethernet, EthernetServer="tcp://xxxx"},
});
CurrentConnectionType = ConnectionTypes[2];
}
private ConnectionTypeViewModel _currentConnectionType;
public ConnectionTypeViewModel CurrentConnectionType
{
get { return _currentConnectionType; }
set
{
_currentConnectionType = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentConnectionType)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ConnectionTypeViewModel : INotifyPropertyChanged
{
private ConnectionType _connectionTypeName;
public ConnectionType ConnectionType
{
get { return _connectionTypeName; }
set { _connectionTypeName = value; OnPropertyChanged(); }
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Rs232ConnectionViewModel : ConnectionTypeViewModel
{
private string _rs232Port;
public string Rs232Port
{
get { return _rs232Port; }
set { _rs232Port = value; OnPropertyChanged(); }
}
}
public class CanConnectionViewModel : ConnectionTypeViewModel
{
private string _canParam;
public string CanParam
{
get { return _canParam; }
set { _canParam = value; OnPropertyChanged(); }
}
}
public class EthernetConnectionViewModel : ConnectionTypeViewModel
{
private string _ethernetServer;
public string EthernetServer
{
get { return _ethernetServer; }
set { _ethernetServer = value; OnPropertyChanged(); }
}
private string _ethernetPort;
public string EthernetPort
{
get { return _ethernetPort; }
set { _ethernetPort = value; OnPropertyChanged(); }
}
}
public enum ConnectionType { Rs232 = 0, Can = 1, Ethernet = 2 };

Related

Data no showing in UserControl

When i run the code, no data is shown in the UI. Data is there in the Person object but not showing in UI.
The list is getting populated. There are 10 items in the person object and i can see 10 items being created in the UI but the name and age are not showing.
In visual studio preview i can see data
UserControl XAML
<UserControl x:Class="BlankApp1.Views.PersonUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Margin="10 3"/>
<TextBlock Text="{Binding Age}" Margin="10 3" Grid.Column="1"/>
</Grid>
</UserControl>
Model
public class PersonModel : ObservableBase
{
private string name;
public string Name
{
get { return name; }
set { Set(ref name, value); }
}
private int age;
public int Age
{
get { return age; }
set { Set(ref age, value); }
}
}
MainViewModel
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
Mock();
}
public ObservableCollection<PersonModel> Persons { get; set; }
= new ObservableCollection<PersonModel>();
private Random rand = new Random();
private void Mock()
{
for (int i = 0; i < 10; i++)
{
Persons.Add(new PersonModel
{
Name = string.Format("Person {0}", i),
Age = rand.Next(20, 50)
});
}
}
}
MainView XAML
<Window x:Class="BlankApp1.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:BlankApp1.ViewModels"
xmlns:model="clr-namespace:BlankApp1"
xmlns:view="clr-namespace:BlankApp1.Views"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Resources>
<DataTemplate DataType="{x:Type model:PersonModel}">
<view:PersonUserControl/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="ItemsControl Example" FontWeight="Bold" Margin="4 10"/>
<Border BorderThickness="1" BorderBrush="Silver" Margin="4" Grid.Row="1">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Persons}"/>
</ScrollViewer>
</Border>
<TextBlock Text="ListBox Example" FontWeight="Bold" Margin="4 10" Grid.Column="1"/>
<ListBox ItemsSource="{Binding Persons}" Margin="4" Grid.Row="1" Grid.Column="1">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</Window>
prism:ViewModelLocator.AutoWireViewModel="True" sets the DataContext of the PersonUserControl to a view model which means that the binding to the properties of the PersonModel object that you create in the MainWindowViewModel fails.
So you should remove prism:ViewModelLocator.AutoWireViewModel="True" from the XAML markup.

Prism 7: Can’t display view

I'm trying to display a view with prism region manager. The problem is that Views are added to the RegionManager and ActiveViews is set but nothing is displaying in MainView(StartWindowView).
My Bootstrapper:
public class Bootstrapper: UnityBootstrapper
{
private readonly Action<Window> aR;
public Bootstrapper(Action<Window> AR)
{
aR = AR;
}
protected override DependencyObject CreateShell()
{
return Container.TryResolve<Views.StartWindowView>();
}
protected override void InitializeShell()
{
aR(Container.TryResolve<Views.StartWindowView>());
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType(typeof(object), typeof(Views.TestView),"PlotListView");
Container.RegisterType(typeof(object), typeof(Views.SettingsView), "SettingsView");
Container.RegisterType<IAutoCadService, AutoCadService>
(new ExternallyControlledLifetimeManager());
Container.RegisterType<IPlotListViewModel, PlotListViewModel>
(new ExternallyControlledLifetimeManager());
}
}
My MainWindow(StartWindowView) with RegionManager.RegionName="ContentRegion":
Window x:Class="PlottingFastAutoCAD2016.Views.StartWindowView"
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"
mc:Ignorable="d"
Background="LightSkyBlue"
x:Name="window"
xmlns:res="clr-namespace:PlottingFastAutoCAD2016.ViewModels"
d:DataContext="{x:Static res:StartWindowViewModelDesign.Instanc}"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
d:DesignHeight="500"
d:DesignWidth="600">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Width="50"
Grid.Column="0"
Padding="0"
HorizontalAlignment="Left"
BorderThickness="0"
Height="Auto"
Click="Button_Click"
Command="{Binding ElementName=window,
Path=DataContext.NavigationCommand}"
CommandParameter="PlotListView"
Background="Transparent">
<StackPanel VerticalAlignment="Stretch"
Margin="3">
<Image Stretch="Fill"
Source="/PlottingFastAutoCAD2016;component/Resources/Icon/printer-.png"/>
<TextBlock Text="Plot"
HorizontalAlignment="Center"
Foreground="White"/>
</StackPanel>
</Button>
<Button Width="50"
Grid.Column="1"
Padding="0"
BorderThickness="0"
Height="Auto"
Command="{Binding ElementName=window,
Path=DataContext.NavigationCommand}"
CommandParameter="SettingsView"
Background="Transparent">
<StackPanel VerticalAlignment="Stretch"
Margin="3">
<Image Stretch="Fill"
Source="/PlottingFastAutoCAD2016;component/Resources/Icon/settings.png"/>
<TextBlock Text="Settings"
HorizontalAlignment="Center"
Foreground="White"/>
</StackPanel>
</Button>
</Grid>
<ItemsControl prism:RegionManager.RegionName="ContentRegion"
Grid.Row="1"
x:Name="contentRegion"
/>
</Grid>
</Window>
My ModelView:
public class StartWindowViewModel : BindableBase, IStartWindowViewModel
{
private readonly IRegionManager regionManager;
public StartWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
NavigationCommand = new DelegateCommand<string>(Navigate);
}
private void Navigate(string uri)
{
regionManager.RequestNavigate("ContentRegion", new Uri(uri, UriKind.Relative),
r => {
if (r != null) ;
//MessageBox.Show("Result: "+r.Result);
else MessageBox.Show("Error: " + r.Error.Message);
});
}
public DelegateCommand<string> NavigationCommand { get; set; }
}
Anyone have idea what is going on? I have spent three days to resolve it but no luck.

Performance Issue with ListControl and Observable Dictionary WPF

I have an itemsControl bound to an observable dictionary, showing both the Key and Value in two textboxes,
It takes about 15 seconds for the usercontrol to load.
I tried virtualizing stackpanels and switching it to either a listbox or using a regular dictionary, and the lag still occurs
Any ideas what might be causing this, and how I can get it to load faster?
public partial class WordsView : UserControl, INotifyPropertyChanged
{
public WordsView()
{
InitializeComponent();
Dictionarylist.ItemsSource = curDic;
}
private ObservableDictionary<string,int> cur_dic = new ObservableDictionary<string, int>(App.PredDic);
public ObservableDictionary<string, int> curDic
{
get { return cur_dic; }
set
{
SetField(ref cur_dic, value, "curDic");
}
}
}
}
and my xaml
<UserControl x:Class="Ez.Management.WordsView"
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:Ez.Management"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
xmlns:properties="clr-namespace:Ez.Properties"
xmlns:main ="clr-namespace:Ez"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="1" Header="Words">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl x:Name="Dictionarylist" ItemsSource="{Binding curDic}" VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Key}" Grid.Column="0" />
<TextBlock Text="{Binding Value}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</GroupBox>
</Grid>
</StackPanel>
</UserControl>
ScrollViewer.CanContentScroll is taking time... Try setting it to False. Like,
ScrollViewer.CanContentScroll="False"

How to crop the elements not fit to a WrapPanel

The example of the WrapPanel.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="1" Style="{StaticResource DestinationItemsControlStyle}"
DataContext="{StaticResource ViewModelKey}"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource DestinationButtonStyle}">
<TextBlock Text="{Binding FullName}" Style="{StaticResource DestinationTextBlockStyle}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
If there are to many elements then all these "excessive" elements become rendered in half of their's real size. Is there a way not to render such the elements?
Am I forced to use something like VirtualizedWrapPanel?
I also want to notice that I can't use a scroll bar. All the "excessive" elements should be rendered on the next page which can be visited by the user's click on the button "Next".
This XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Name="ItemsControl1" Grid.Row="1"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="20" MinHeight="50" MinWidth="400">
<TextBlock Text="{Binding FullName}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
and the addition of 70 items in the Stations collection at startup, produces this result:
What does your DestinationItemsControlStyle look like?
For reference, this is the code-behind that runs on Startup:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModelKey vmk = new ViewModelKey();
ItemsControl1.DataContext = vmk;
}
}
and in the ViewModelKey class:
public class ViewModelKey : INotifyPropertyChanged
{
public ObservableCollection<station> Stations { get; set; }
public ViewModelKey()
{
Stations = new ObservableCollection<station>();
for (int i = 1; i < 70; i++)
{
Stations.Add(new station("This is station " + i.ToString()));
}
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

ItemsControl Grid.Row Grid.Column Binding in Silverlight

I'm trying to fill a Grid using different X and Y values for Grid.Row and Grid.Column using an ItemsControl, I copied it from a WPF project of mine and can't get it to work in Silverlight (Windows Phone).
Here is a simplified version of it:
ViewModel which the DataContext is set to:
public class ViewModel
{
public ObservableCollection<GridItem> Data { get; set; }
public ViewModel()
{
Data = new ObservableCollection<GridItem>();
FillData();
}
// fill Data property with some random color GridItems
private void FillData()
{
string[] colors = { "Red", "Green", "Yellow", "Blue" };
Random r = new Random();
for (int i = 0; i < 10; i++)
{
Data.Add(new GridItem(i, i, colors[r.Next(colors.Length)]));
}
}
}
public class GridItem
{
public int X { get; set; }
public int Y { get; set; }
public string Color { get; set; }
public GridItem(int x, int y, string color)
{
X = x;
Y = y;
Color = color;
}
}
The XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ItemsControl ItemsSource="{Binding Data}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Background="Orange">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse
Grid.Row="{Binding Y}"
Grid.Column="{Binding X}"
Fill="{Binding Color}"
Stroke="White" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
This is the result (it placed all ellipses on top of eachother):
While it should have done this:
Does anyone know why this doesn't work?
Try setting the binding for the Grid.Row and Grid.Column within the ContentPresenter rather than as part of the DataTemplate that defines the Ellipse:
<ItemsControl.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Row" Value="{Binding Y}"/>
<Setter Property="Grid.Column" Value="{Binding X}"/>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse
Fill="{Binding Color}"
Stroke="White" />
</DataTemplate>
</ItemsControl.ItemTemplate>
It's because the DataTemplate wraps its content in a ContentPresenter. The Grid.Row and Grid.Column properties only apply to direct descendants so in this case they are not relevant because they are two levels deep in the control hierarchy.
The ContentPresenter is added dynamically at run time when the DataTemplate renders. To make this work you'd need to hook into the dynamic generation of the items and set the Row and Column attached properties on the containing item.
There is a blog post showing one way of getting this working at: http://www.scottlogic.co.uk/blog/colin/2010/11/using-a-grid-as-the-panel-for-an-itemscontrol/

Resources