Design-time data for ItemsControl's DataTemplate - wpf

I want to use some mock data to design my DataTemplate. How do I set a mock ObservableCollection as the ItemsSource of my ItemsControl, considering I'm using d:DataContext on it to point to a mock class containing said collection?
Here is what I have so far:
<DataTemplate x:Key="MyTemplate">
<Grid Margin="5,5,5,5">
<CheckBox Content="{Binding Name}" />
</Grid>
</DataTemplate>
<ItemsControl d:DataContext="{d:DesignInstance Type=mocks:MyViewModelMock, IsDesignTimeCreatable=True}" ItemsSource="{Binding MyMockList}" ItemTemplate="{StaticResource MyTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
public class MyViewModelMock {
public ObservableCollection<MyModel> MyMockList { get; set; }
public MyViewModelMock() {
MyMockList .Add(new MyModel() { Name = "Mock 1" });
MyMockList .Add(new MyModel() { Name = "Mock 2" });
}
}

Point you d:DataContext at a static implementation of that type and you will have your design time data context. Here is a good example http://adamprescott.net/2012/09/12/design-time-data-binding-in-wpf/

Related

Binding several ItemsControl to one ObservableCollection including visibility

In general, we can not bind multiple controls to one ObservableCollection
Is it possible to do this in the following situation?
Only one part is visible at a time
In this situation, there is a reference error twice to the same collection
How does it actually work internally? Should it not include invisible code?
<Grid Visibility="{Binding B1Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding Elements, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--Another code for B1-->
</Grid>
<Grid Visibility="{Binding B2Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding Elements, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!--Another code for B2 -->
</Grid>
I don't know what Elements is, and I don't know where you're headed using a grid for the itemspanel of your itemscontrol.
But.
You can bind to the same observablecollection multiple times.
In the code below the two visibility properties are Boolean and the converter translates true into Visibility.Visible and false into Visibility.Collapsed.
public class MainWindowViewModel : BaseViewModel
{
private ObservableCollection<Person> people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return people; }
set { people = value; RaisePropertyChanged();}
}
private bool b1Visible = true;
public bool B1Visible
{
get { return b1Visible; }
set { b1Visible = value; RaisePropertyChanged(); }
}
private bool b2Visible = true;
public bool B2Visible
{
get { return b2Visible; }
set { b2Visible = value; RaisePropertyChanged(); }
}
public MainWindowViewModel()
{
People.Add(new Person { FirstName = "Chesney", LastName = "Brown" });
People.Add(new Person { FirstName = "Gary", LastName = "Windass" });
People.Add(new Person { FirstName = "Liz", LastName = "McDonald" });
People.Add(new Person { FirstName = "Carla", LastName = "Connor" });
}
}
My markup:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<Grid Visibility="{Binding B1Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--Another code for B1-->
</Grid>
<Grid Visibility="{Binding B2Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
Both itemscontrols are visible and so I see the list of names twice.

Bind Usercontrol contained in a ObservableCollection<CustomClass> in a WrapPanel

I have a class defined this way:
public class CustomClass
{
string Name;
int Age;
Usercontrol usercontrol;
}
where Usercontrol is a visual element that I want to insert in a WrapPanel.
CustomClass is organized in a static ObservableCollection.
public static class CollectionClass
{
public static ObservableCollection<CustomClass> MyCollection = new ObservableCollection<CustomClass>();
}
I am looking to bind the usercontrol property of the CustomClass in the collection to be visualized in the WrapPanel, so I have the visual elements showed in the same order as the elements in the collection.
Right now I am populating the WrapPanel manually by code, but I figured that there has to be a way to do it quickly and easily through databinding.
I am trying to do it with a ItemsControl defined this way:
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
but I don't know how to make the magic happen.
EDIT:
I tried the solution proposed by ChrisO, implemented like that:
1 - I made the collection a property
2 - I made the UserControl a property
3 - I set DataContex from code:
SensorMenuWrap.Datacontext = CollectionClass.MyCollection
4 - The binding:
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding usercontrol}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now I can visualize the first element of the collection. If the first element changes, I visualize the new first element. How can I visualize the entire collection?
I haven't tested this. Also, make sure that userControl is a property rather than a field. I'm assuming you know how to set the DataContext up correctly to refer to the MyCollection property.
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding userControl}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You should also consider referring directly to the UserControl in the DataTemplate rather than binding to it as a property on the class. That would look like this
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit:
In response to your edit, you would be much better tackling this with a reusable UserControl with DataBindings on the properties of CustomClass. Here's what I have to achieve that:
CollectionClass
public static class CollectionClass
{
public static ObservableCollection<CustomClass> MyCollection { get; set; }
static CollectionClass()
{
MyCollection = new ObservableCollection<CustomClass>();
MyCollection.Add(new CustomClass { Age = 25, Name = "Hamma"});
MyCollection.Add(new CustomClass { Age = 32, Name = "ChrisO"});
}
}
CustomClass
public class CustomClass
{
public string Name { get; set; }
public int Age { get; set; }
}
UserControl contents
<StackPanel>
<TextBlock Background="Tan" Text="{Binding Name}" />
<TextBlock Background="Tan" Text="{Binding Age}" />
</StackPanel>
MainWindow
<ItemsControl Name="SensorMenuWrap" ItemsSource="{Binding }">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<so25728310:Usercontrol Margin="5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here, instead of having the Usercontrol baked into your data, you're keeping it in the view. It's always good to have a separation between your view and data. You could even do away with the Usercontrol and have the two TextBoxes directly in the DataTemplate but it depends on whether you would want to reuse that view elsewhere.

Reusable usercontrol with radiobuttons are being unchecked unexpectedly

I'm having an issue with the RadioButton when using a reusable UserControl. For some reason I am unable to check a radio button of each 'Chooser' control but instead when one radio button is checked, all other radio buttons in the current chooser and in the other choosers are unchecked. Does anyone know how to change this code so that I can have a checked item in each 'chooser' user control. The user control must be able to be dynamically built using a collection. In a real world example, each 'chooser' usercontrol will have different text values.
MainPage.xaml
<StackPanel x:Name="LayoutRoot" Background="White">
<radioButtonTest:Chooser />
<radioButtonTest:Chooser />
<radioButtonTest:Chooser />
</StackPanel>
Chooser.xaml
<UserControl x:Class="RadioButtonTest.Chooser"
xmlns ...>
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
<TextBlock x:Name="MyLabel" Text="Choices:" VerticalAlignment="Center" Margin="0,0,10,0" />
<ItemsControl x:Name="MyChooser" Height="25" VerticalAlignment="Top" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Height="22" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="{Binding}" MinWidth="35" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>
Chooser.xaml.cs
public partial class Chooser
{
public Chooser()
{
InitializeComponent();
// adding some test items to the itemscontrol
MyChooser.ItemsSource = new ObservableCollection<string>
{
"first",
"second",
"third"
};
}
}
Turns out I needed to use the GroupName property of the RadioButton to dictate the groupings. I did this by changing the item source to a custom class with a Group property of type string and binding this property to the GroupName property on the RadioButton.
XAML:
<DataTemplate>
<RadioButton ... Content="{Binding Name}" GroupName="{Binding Group}" />
</DataTemplate>
C#:
public Chooser()
{
InitializeComponent();
// the groupName needs to be the same for each item
// in the radio group, but unique for each separate group.
var groupName = Guid.NewGuid().ToString();
MyChooser.ItemsSource = new ObservableCollection<RadioButtonGroupItem>
{
new RadioButtonGroupItem {Group = groupName, Name = "first"},
new RadioButtonGroupItem {Group = groupName, Name = "second"},
new RadioButtonGroupItem {Group = groupName, Name = "third"}
};
}
public class RadioButtonGroupItem
{
public string Name { get; set; }
public string Group { get; set; }
}
Hope this helps someone.

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!

Binding Hierarchical object data to ListBox

I'm trying to populate a ListBox with data from an object source using data binding in WPF.
The source is an ObjectDataProvider whose data is loaded in from an xml file. I read in the XML file, filling in the appropriate data structure, then set the ObjectInstance for the ObjectDataProvider to the data structure.
Here is the data structure:
public class Element
{
public decimal myValue;
public decimal df_myValue { get { return myValue; } set { this.myValue = value; } }
}
public class BasicSet
{
public Element[] elementSet;
public Element[] df_elementSet { get { return elementSet; } set { this.elementSet = value; } }
}
public class DataSet
{
public BasicSet[] basicSet;
public BasicSet[] df_basicSet { get { return basicSet; } set { this.basicSet = value; } }
}
Here is the XAML:
<UserControl.Resources>
<ResourceDictionary>
<ObjectDataProvider x:Key="TheData" />
<DataTemplate x:Key="ElementTemplate">
<StackPanel>
<TextBox Text="{Binding, Path=df_myValue}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="ElementSetTemplate" ItemsSource="{Binding Path=df_elementSet}" ItemTemplate="{StaticResource ElementTemplate}">
</HierarchicalDataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ListBox ItemsSource="{StaticResource TheData}" ItemTemplate="{StaticResource ElementSetTemplate}">
</ListBox>
</Grid>
Here is the code-behind where the xml data is being loaded:
private DataSet m_dataSet;
private ObjectDataProvider mODP;
public void LoadXml(EditorContext context, XmlValidator.Context validator, XmlDocument doc)
{
mODP = FindResource("TheData") as ObjectDataProvider;
XmlSerializer xs = new XmlSerializer(typeof(DataSet));
XmlReader r = new XmlNodeReader(doc.DocumentElement);
m_dataSet = (DataSet)xs.Deserialize(r);
mODP.ObjectInstance = m_dataSet;
}
The desired result is that the ListBox would have a TextBox for each element in the data structure. Note that the data structure is hierarchical for a reason. I cannot flatten the data structure to simplify the problem.
I am certain that the xml data is being loaded correctly into the data structure, because I can place a breakpoint and check it and all the data looks fine. But when I run the program, nothing shows up in the ListBox.
Any help is appreciated.
I figured out the things I was doing wrong. There were several things wrong. Here are the main points:
1) I should have been using an itemsControl
2) I didn't need to use HierarchicalDataTemplate
3) I needed three levels of controls: a TextBox inside an itemsControl inside an itemsControl... and this can be accomplished using two DataTemplates. The top-level itemsControl refers to a DataTemplate that holds an inner itemsControl. That itemsControl then refers to a DataTemplate that holds an inner TextBox.
And here is the correct xaml:
<UserControl.Resources>
<ResourceDictionary>
<ObjectDataProvider x:Key="TheData" />
<DataTemplate x:Key="ElementTemplate">
<TextBox Text="{Binding Path=df_myValue}"/>
</DataTemplate>
<DataTemplate x:Key="ElementSetTemplate">
<GroupBox Header="I am a GroupBox">
<ItemsControl ItemsSource="{Binding Path=df_elementSet}" ItemTemplate="{StaticResource ElementTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Source={StaticResource TheData}, Path=df_basicSet}" ItemTemplate="{StaticResource ElementSetTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
Hope this helps someone else...

Resources