Suggestions for synchronization of lists in Silverlight - silverlight

We are creating a business application (Silverlight 4) at our company where we, in several of our views, use the functionality to synchronize two lists of some kind.
More precise we are, at the moment, using two list boxes (one source and one target) and two buttons (one add and one remove). Both the source list and the target lists are bound to collections with the same data type.
A user may select one or more items in the source list and press the add button to have the items moved to the target list (i.e. the items are removed from the source and added to the target).
Likewise the user could select one or more items in the target list and press remove to have the items moved from the target back to the source list.
There is also the possibility to add a validation rule that says that the user must add at least one item to the target list.
Pretty simple...
Until now we are using our own created user control which incapsulates these 4 controls (2 list boxes and 2 buttons) and the logic for keeping the lists in sync. Dependency properties are used for binding the source and target collections.
Now to the problem. Our customer now wants our user control to be more flexible.
They want to be able to have an arbitray number of columns in both the source and target list (i.e. the source list may have different columns and a different number of columns than the target list). The customer also wants to be able to sort on any column.
My first thought was to replace the list box to the data grid instead. But then I realized I don't know how to, in an easy way, let the consumer (the developer) define his or her columns and bindings. This may be to my limited knowledge of SL. Maybe a custom user control isn't the way to go?
I would appreciate any kind of help. Right now we are implementing the same logic over and over again in our views and it doesn't feel right. There has to be some way we can make this a reusable component that is easy to use.
Thanks!

"Grid version" of the answer:
(see list version below)
As the question has changed (clarified) I am adding a new answer. The first one is still useful for those that only want lists so I will leave it there.
To do a similar thing with grids you don't expose the templates as datagrid columns are not templated (they are lists of controls, which can individually be templated).
Instead you expose the left grid and right grid column collections as properties and simply set the LeftColumns and RightColumns properties of the control in your parent page. The only trick is that the datagrid-column collections have no setter so you need to update the grid collections items from the new property collections:
The code-behind is now:
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class GridSelectionControl : UserControl
{
public GridSelectionControl()
{
InitializeComponent();
}
public ObservableCollection<DataGridColumn> LeftColumns
{
get
{
return leftDataGrid.Columns;
}
set
{
leftDataGrid.Columns.Clear();
foreach (var col in value)
{
leftDataGrid.Columns.Add(col);
}
}
}
public ObservableCollection<DataGridColumn> RightColumns
{
get
{
return rightDataGrid.Columns;
}
set
{
rightDataGrid.Columns.Clear();
foreach (var col in value)
{
rightDataGrid.Columns.Add(col);
}
}
}
}
}
The Xaml of the control is now:
<UserControl
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.GridSelectionControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<sdk:DataGrid x:Name="leftDataGrid" Margin="10" AutoGenerateColumns="False"/>
<sdk:DataGrid x:Name="rightDataGrid" Margin="10" Grid.Column="2" AutoGenerateColumns="False"/>
<StackPanel Grid.Column="1" Orientation="Vertical">
<Button Content="Add >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
</StackPanel>
</Grid>
</UserControl>
The Xaml of the test page (which defines the columns) is now:
<UserControl
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:SilverlightApplication1" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
x:Class="SilverlightApplication1.TestGridSelectionControl"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<local:GridSelectionControl x:Name="SelectionControl">
<local:GridSelectionControl.LeftColumns>
<sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
<sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
</local:GridSelectionControl.LeftColumns>
<local:GridSelectionControl.RightColumns>
<sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
<sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
</local:GridSelectionControl.RightColumns>
</local:GridSelectionControl>
</Grid>
</UserControl>
And the test code-behind is now:
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class TestGridSelectionControl : UserControl
{
public TestGridSelectionControl()
{
// Required to initialize variables
InitializeComponent();
SelectionControl.leftDataGrid.ItemsSource = Person.People;
SelectionControl.rightDataGrid.ItemsSource = Person.Machines;
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsMachine { get; set; }
public Person(string firstname, string lastname, bool robot)
{
this.FirstName = firstname;
this.LastName = lastname;
this.IsMachine = robot;
}
public static ObservableCollection<Person> People = new ObservableCollection<Person>()
{
new Person("Tom", "Jones", false),
new Person("Elis", "Presley", false),
new Person("Joe", "Blogs", false)
};
public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
{
new Person("Marvin", "Android", true),
new Person("Hal", "9000", true),
new Person("B", "9", true)
};
}
}
}

Since you are creating a control to be consumed by other developers then its usually best to be using a Template Control rather than a UserControl. In which case the developers can specify a custom template for the control. However that's not as helpful as you could be, especially if the set of headers for both grids are the same.
One approach you could take is to provide dependency property of the type DataTemplate called "ListTemplate". At the two points in your controls Xaml where you would display the lists use two ControlPresenter elements. One named "SourceContent" the other "TargetContent". For both bind ContentTemplate to this new "ListTemplate".
Code up the assignment of the Content property on these presenters and then assign the appropriate collection the ItemsSource of either the ItemsControl or DataGrid the presenter has loaded.
If you include a simple ListBox base data template as the default value for the "ListTemplate" property then you control should be usuable in its simplest form yet if the developer wants use a DataGrid with various columns they can define one in the
ListTemplate property.
Of course you will need to write code in your control to cope with the lists possibly being a DataGrid elements.

"List version" of the answer
(see also the grid version)
A custom control sounds right for this, but you want both lists to be templated so the developer can define the per item views. I have assumed you do not need headings so have stuck with listboxes. The test code below results in this:
As the listboxes already have item templates, you really want to expose those as properties in your custom user control. You can then edit the 2 templates individually (the example below simply has both Left and Right templates set to the same FirstName/LastName stackpanel, this is where you define the format of your listboxes):
<UserControl
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:SilverlightApplication1"
mc:Ignorable="d"
x:Class="SilverlightApplication1.TestListSelectionControl"
d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate1">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding FirstName}"/>
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding LastName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<local:ListSelectionControl x:Name="SelectionControl" d:LayoutOverrides="Height" LeftItemTemplate="{StaticResource DataTemplate1}" RightItemTemplate="{StaticResource DataTemplate1}"/>
</Grid>
</UserControl>
The example ListSelectionControl XAML is below:
<UserControl x:Class="SilverlightApplication1.ListSelectionControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox HorizontalAlignment="Stretch" Margin="12" Name="leftListBox" VerticalAlignment="Stretch" />
<ListBox HorizontalAlignment="Stretch" Margin="12" Name="rightListBox" VerticalAlignment="Stretch" Grid.Column="2" />
<StackPanel Grid.Column="1" Orientation="Vertical">
<Button Content="Add >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
</StackPanel>
</Grid>
</UserControl>
And the simple code-behind of the control:
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class ListSelectionControl : UserControl
{
public DataTemplate LeftItemTemplate
{
get
{
return leftListBox.ItemTemplate;
}
set
{
leftListBox.ItemTemplate = value;
}
}
public DataTemplate RightItemTemplate
{
get
{
return rightListBox.ItemTemplate;
}
set
{
rightListBox.ItemTemplate = value;
}
}
public ListSelectionControl()
{
InitializeComponent();
}
}
}
And just to complete the example, this is the code behind to populate the sample GUI:
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace SilverlightApplication1
{
public partial class TestListSelectionControl : UserControl
{
public TestListSelectionControl()
{
// Required to initialize variables
InitializeComponent();
SelectionControl.leftListBox.ItemsSource = Person.People;
SelectionControl.rightListBox.ItemsSource = Person.Machines;
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstname, string lastname)
{
this.FirstName = firstname;
this.LastName = lastname;
}
public static ObservableCollection<Person> People = new ObservableCollection<Person>()
{
new Person("Tom", "Jones"),
new Person("Elis", "Presley"),
new Person("Joe", "Blogs")
};
public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
{
new Person("Marvin", "Android"),
new Person("Hal", "9000"),
new Person("B", "9")
};
}
}

Related

Why is ItemsControl in the following WPF XAML not showing anything?

When I run the app, I expect to see 5 buttons (see ItemsControl\DataTemplate\Button in my XAML below) each with a content like "55/42" denoting max ad min temperature. However, the window is blank. I know this has to do with ItemsControl because I can display the data without using the ItemsControl. Can someone catch my mistake?
<Window x:Class="Embed_WeatherSummaryAsItemsControl_ToMain.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusManager.FocusedElement="{Binding ElementName=InputCity}"
Title="Weather App" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBox x:Name="InputCity" Grid.Row="0" Width="200" Text="{Binding CityAndOptionalCountry}"></TextBox>
<Button Grid.Row="0" Width="50" Content="Go" Margin="260,0,0,0" Command="{Binding GetWeatherReportCommand}"></Button>
<ItemsControl Grid.Row="1" ItemsSource="{Binding WeatherForecastSummaryCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Path=MaxMinTemperature}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
As shown below, "WeatherForecastSummaryCollection" is a collection property on ViewModel class and "MaxMinTemperature" is a property of item in collection.
public class MainWindowViewModel : ViewModelBase
{
....
private List<WeatherForecastSummary> mWeatherForecastSummaryCollection;
public List<WeatherForecastSummary> WeatherForecastSummaryCollection
{
get { return mWeatherForecastSummaryCollection; }
set
{
mWeatherForecastSummaryCollection = value;
OnPropertyChanged("WeatherForecastSummaryCollection");
}
}
.....
}
public class WeatherForecastSummary
{
public string MaxMinTemperature { get; set; }
}
Thanks for helping!
I think you are missing the DataContext here.
Even if you are using the codebehind of the View, you need to write
this.DataContext = this;
or if you are using someother class as viewmodel you might want to point the datacontext to that class. Just replace "this" in the above code with your viewmodels object.
In this case it would be ,
this.DataContext = new MainWindowViewModel();

Getting weird behaviours when changing the binding to the TreeView

I have ObservableCollection Fathers which contains property ObservableCollection Sons.
And I'm displaying it on the TreeView setting its DataContext property.
The Sons property displays as a ListBox of radio button under each Father - binded to ItemsSource.
First time setting the DataContext of the tree view to the fathers list, everything is working good. The radio buttons are checked according to the data.
Now, I'm setting the TreeView.DataContext to null - so the data will disappear. and then back to the original Fathers ObservableCollection which I set in the first time.
And now from some reason the radio buttons stopped being synchronized with the son object.
And I got deeper and I saw that the setter in the son object (that binded to the radio button) is raised with false from some reason. I Guess something related to the binding.
Is there any cache that the TreeView, or the ObservableCollection is saving after binding ? I want it to work like the first time I set the bind - Which there like it should only the getter is being called like it should.
Thanks.
This is my Tree view
<UserControl x:Class="Tester.CTLMyTree"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="Check" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
</Grid>
</UserControl>
The Objects behind
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
}
In my application i added this treeview control and called it m_objSimpleTree.
This code is the initializing
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = true;
m_objItems.Add(objItem1);
m_objSimpleTree.myTreeView.DataContext = m_objItems;
And when i press a button i'm doing this
m_objSimpleTree.myTreeView.DataContext = null;
m_objSimpleTree.myTreeView.DataContext = m_objItems;
This code will raise already the IsChecked setter of the son to false (Why ???)
But the RadioButton will still be checked.
Second time pressing the button. it will be unchecked and the setter didn't raise.
When i'm pressing on the radio button It's raising twice the setter. First time with false
second with true.
Can't figure why it's happening.. The only think i can think of is that the treeview is saving something in the first binding or something like this.
It does so because you have used twoWay binding for the control
binding code project
In a two way binding when you change some thig on the view then the data gets saved in the object also . to which the datacontext is assigned. try oneWay for that . But need to be careful as if you wanna save data using twoWay one way might not help. MVVM suggests to use two way binding to save data but you want a refreshed list then create a new object :)
Also try clear Binding
Clear binding
Am not sure of the last link as never tried. Please o through it might get an idea. But since you might need a new object again so you can create a fresh object to assign to datacontext.
--- EDIT-----
Here's a xaml code
<UserControl
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightSOApp.MainPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked2, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
<Button Content="Button" HorizontalAlignment="Left" Margin="303,268,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
And the c#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightSOApp
{
public partial class MainPage : UserControl
{
private ObservableCollection<CCFather> m_objItems;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = false;
m_objItems.Add(objItem1);
myTreeView.DataContext = m_objItems;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myTreeView.DataContext = null;
myTreeView.DataContext = m_objItems;
}
}
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
private bool m_blnChecked2;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
public bool IsChecked2
{
get
{
return m_blnChecked2;
}
set
{
m_blnChecked2 = value;
}
}
}
}
Now the main point if you want to implement it for single radio button then you need to implement the Click event and set radio button to false and next time to true :) or else you need to use a checkbox one radio button once checked cannot be converted to false

WPF-MVVM Binding ViewModel-Property to nested UserControl

As the title says, I want to bind a property from my ViewModel to a nested UserControl in the corresponding view.
I cant get it work the way I need.
The nested UserControl is nothing more than a DatePicker and a DropDown for the hours. How can I tell the DatePicker to choose the date propagated by the ViewModel as its selected date?
I tried nearly everything and now I'm not far away from jumping outside the window.
As you can see any help is appreciated ;)
Now to the code so far: DateTimePicker.xaml.cs (CodeBehind)
public partial class DateTimePicker
{
public static DependencyProperty SelectedDateValueProperty = DependencyProperty.Register("SelectedDateValue", typeof (DateTime), typeof (DateTimePicker), new FrameworkPropertyMetadata(default(DateTime), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPropertyChangedCallback));
private static void OnPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
Debug.WriteLine("Wohoo. I'm here and still debugging...");
}
public DateTimePicker()
{
InitializeComponent();
DataContext = this;
var times = GetTimes();
Times.ItemsSource = times;
Times.SelectedItem = times.First();
}
public DateTime SelectedDateValue
{
get { return (DateTime) GetValue(SelectedDateValueProperty); }
set { SetValue(SelectedDateValueProperty, value); }
}
}
The nested UserControl (DateTimePicker.xaml):
<UserControl x:Class="Framework.Controls.DateTimePicker"
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"
d:DesignHeight="30" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<DatePicker HorizontalAlignment="Left" Name="DatePickerCalendar" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" SelectedDate="{Binding SelectedDateValue}" />
<ComboBox Grid.Column="1" VerticalAlignment="Center" Name="Times" DisplayMemberPath="Name" />
</Grid>
And, last but not least: The View which has the nested UserControl (View.xaml)
<CustomControls:DateTimePicker SelectedDateValue="{Binding LocalRegistrationStartDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Hope the problem is clear and anybody can help me or get the point at what i am doing wrong here.
Using:
"{Binding SelectedDateValue}"
tells WPF "Hey check my DataContext for a property called SelectedDateValue".
What you want is, to get the Property from your user control.
The easiest way is to give your user control a name like:
<UserControl x:Name="myControl"/>
and then modify your binding to :
"{Binding ElementName=myControl, Path=SelectedDateValue}"
The usual way WPF controls are implemented is to use a template rather than defining the control as direct content, like you're doing here. By using a Template, you have access to TemplateBinding, allowing you to easily bind your control properties. See the Control Customization MSDN page.
<ControlTemplate TargetType="{x:Type local:DateTimePicker>
...
<DatePicker SelectedDate="{TemplateBinding SelectedDateValue}" />
...
</ControlTemplate>

Silverlight PropertyGrid-like binding

I know that there are 3rd party silverlight property grids (in fact my company owns one) so please dont suggest 3rd party controls : I am trying to learn more about binding in xaml with this question.
I am writing a Silverlight front end as a sign facade to launch SSRS, and php based reports.
I have created a Report class with information about the report, and it has a parameters collection containing the information about parameters that need to be filled in to run the report.
My plan is to create a silverlight property grid that is bound to the Parameters collection of the Report.
Here's a simpler version of the classes:
public class Report
{
public int ReportId { get; set; }
public string ReportName { get; set; }
public string Description { get; set; }
private List<ReportParameter> _Parameters = new List<ReportParameter>();
public List<ReportParameter> Parameters
{
get { return _Parameters; }
set { _Parameters = value; }
}
}
public class ReportParameter
{
public int ReportId { get; set; }
public string ParameterName { get; set; }
public string DataTemplateName { get; set; }
public bool IsRequired { get; set; }
}
I was hoping to use the DataTemplateName property of ReportParameter to bind to data templates: for example if I have a parameter that is a date, I want to be able to set DataTemplateName="MyDatePicker" and then DataTemplate={StaticResource {Binding DataTemplateName}} and have that row use a DataTemplate defined in the Resources for editing the parameter value.
Here's some XAML I am using to try to get it to work:
<UserControl x:Class="ReportLauncherWorkbench.MainPage"
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:ReportLauncherWorkbench"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:Report x:Key="MyData"
ReportName="Rick Report"
Description="Great report, try it!"
ReportId="0"
>
<local:Report.Parameters>
<local:XReportParameter DataTemplateName="DatePickerTemplate"
ParameterName="StartDate"
IsRequired="True"
Tooltip="Please enter the start date"
/>
<local:XReportParameter DataTemplateName="CheckBoxTemplate"
ParameterName="AmIHot"
IsRequired="True"
Tooltip="Please check here if you are hot"
/>
</local:Report.Parameters>
</local:Report>
<DataTemplate x:Key="DatePickerTemplate">
<StackPanel Orientation="Horizontal">
<TextBox />
<Button Content="..."/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="CheckBoxTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource MyData}">
<Grid x:Name="Test1" Background="White">
<StackPanel>
<TextBlock Text="{Binding ReportName}"/>
<TextBlock Text="{Binding Description}"/>
<ListBox ItemsSource="{Binding Parameters}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ParameterName}" Grid.Column="1" Margin="5"/>
<ListBox ItemsSource="{Binding}" Grid.Column="2" Width="100" ItemTemplate="{StaticResource {Binding DataTemplateName}}">
<!-- I want to somehow bind which DataTemplate is rendered-->
</ListBox>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Grid>
</UserControl>
Thanks!
I figured it out - with the help of a great FAQ on the silverlight forum:
http://forums.silverlight.net/p/95440/218611.aspx
Silverlight does not have the DataTemplateSelector class that WPF does, which would have solved the problem.
In section titled
7.1 What data binding features of WPF are not yet supported in Silverlight? Is there a workaround?
There is a simple workaround that for DataTemplateSelector functionality.
So here's how I fixed it in my code sample:
Replace the Listbox with the following:
<ListBox ItemsSource="{Binding Parameters}">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" Loaded="ContentControl_Loaded"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And then fill in the ContentControl_Loaded event in the code behind with:
private void ContentControl_Loaded(object sender, RoutedEventArgs e)
{
ContentControl cc = (ContentControl) sender;
XReportParameter p = (XReportParameter)cc.DataContext;
cc.ContentTemplate = (DataTemplate)this.Resources[p.DataTemplateName];
}
Works great!

How to filter/find string associated with items from the collection and place that matched string into texblock in Silverlight?

I am wondering if someone can share with information or good sample with filtering listboxitems based on what is typed in the textbox. Perhaps, it can be a different control that fits better to the scenario below.
In my scenario, I need to type a short string in texblock. Then, click ‘check’ button which will find the closest string values of items from the collection and show these matches in a form of the list below the textblock. Selecting any of items from shown list of items will place the selected string/item in the tetxblock. The behavior is very similar to combox box.
Finally, I need to be able to add that selected string/item that was placed in the texblock to another listbox by clicking ‘Add’ button. Any ideas are highly appreciated. Thank you in advance!
Below is my XAML code:
<UserControl
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"
x:Class="FilterListItems.MainPage"
xmlns:local="clr-namespace:FilterListItems"
Width="640" Height="480">
<UserControl.Resources>
<local:Products x:Key="productCollection" />
<CollectionViewSource x:Key="collProducts" Source="{Binding Source={StaticResource productCollection}, Path=DataCollection}">
</CollectionViewSource>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="10" Grid.Row="0">
<TextBlock Text="Enter Partial Name: " />
<TextBox Width="100" Name="txtName" />
<Button Name="btnSearch" Content="Check" Click="btn_Check" />
<Button Name="btnAdd" Content="Add" Click="btn_Add" Margin="9,0,0,0" />
</StackPanel>
<ListBox Margin="10" Grid.Row="1" Name="lstData" DisplayMemberPath="ProductName" ItemsSource="{Binding Source={StaticResource collProducts}}" Visibility="Collapsed" />
<ListBox Margin="10" Grid.Row="2" Name="2stData" />
C# to generate the collection:
public partial class MainPage : UserControl
{
public MainPage()
{
// Required to initialize variables
InitializeComponent();
}
private void btnSearch_Click(object sender, RoutedEventArgs e)
{
//FilterData();
}
}
public class Product
{
public Product(int id, string name)
{
ProductId = id;
ProductName = name;
}
public int ProductId { get; set; }
public string ProductName { get; set; }
}
public class Products : List<Product>
{
public Products()
{
InitCollection();
}
public List<Product> DataCollection { get; set; }
List<Product> InitCollection()
{
DataCollection = new List<Product>();
DataCollection.Add(new Product(1, "aaa"));
DataCollection.Add(new Product(2, "bbb"));
DataCollection.Add(new Product(3, "ccc"));
DataCollection.Add(new Product(4, "ddd"));
DataCollection.Add(new Product(5, "eee"));
DataCollection.Add(new Product(6, "fff"));
DataCollection.Add(new Product(7, "hhh"));
DataCollection.Add(new Product(8, "ggg"));
return DataCollection;
}
}
Check out the AutoCompleteBox from wpftoolkit
also check
http://www.jeff.wilcox.name/2010/02/wpfautocompletebox/
Bea Stollnitz is a great resource for finding out how to filter collections. You should start with her post on "How do I filter items from a collection?" to get a simple and clear picture. When you are done with that article just use the search on her blog to search for "filter"ing collections using collectionviewsource.

Resources