Can you bind in XAML to a Class that takes a type T?
If I have a class myCLASS<T> and I want to bind a List<myClass<int>> to XAML can you do it?
Or do you have to write a wrapper class newMyClass: myClass<int> and then bind the XAML to newMyClass?
Thanks.
The WPF binding subsystem supports any type of object, structures, classes, generic classes and even dynamic objects. All that matters is that the instances have properties.
You cannot create generic classes in the resources of a typical WPF application because the object creation syntax does not support it. Nevertheless, objects that are created in code-behind, in your view-model, or by services (even if they are instances of generic types or nested generic types), can still participate in binding.
Here's an example based on the question. Here's some XAML for a window:
<Grid>
<ListBox ItemsSource="{Binding}" Padding="10">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding X}"/>
<TextBlock Text=" , "/>
<TextBlock Text="{Binding Y}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
and here's a generic class that resembles a Point class:
class myClass<T>
{
public T X { get; set; }
public T Y { get; set; }
}
and here is some code-behind to support the binding in the XAML above:
void Window_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new List<myClass<int>>
{
new myClass<int> { X = 1, Y = 2 },
new myClass<int> { X = 3, Y = 4 },
};
}
and this is what it looks like:
We created instances of the generic class in the code-behind but bound to that data using XAML.
XAML 2009 supports it using x:TypeArguments, but it isn't an comfortable option at the moment since it isn't supported by Microsoft's XAML to BAML compiler. For XAML 2009, you would have to read the XAML using XamlReader yourself, but then lose greater functionality like a code behind. You're best off doing as you suggested at the moment.
Related
I am currently transferring my app from WinForms to WPF.
Since I'm new in WPF, I stucked at creating DataTemplates for my treeView items. The screenshot shows how my treeview looked in WinForms version, and I need to get close result in WPF.
(My WinForms treeview)
As you can see, my DataTemplate's logic should take into account these factors:
Node type / defines which icon and fields combination will be displayed for particular item (node). App has about 7-8 node types. Type stored in separate node's field.
Variable values / I need to replace with text if null, etc
Numeric variable values / e.g.: set gray color if zero, etc.
Other properties / e.g.: adding textblocks depending on boolean fields.
And so on...
All these factors result into huge amount of possible item params combinations.
Also I'm using DevComponents WPF DotNetBar AdvTree to divide item properties into columns. I presume I should create 'sub templates' for different field sets and compose from them the entire DataTemplate for each column.
I've learned about triggers, and have to say that implementing my logic with triggers will make my subtemplates huge anyway.
(Current state of my WPF treeview)
So here are my questions:
Are there any ways to dynamically compose complex templates with C# code (without creating raw XAML and loading it at runtime)?
Maybe I should use completely different way (instead of using DataTemplate)? In Winforms I just used OwnerDraw mode, so the task was MUCH easier than in WPF :(
And how to display nested properties inside template? e.g.: Item.Prop.Subprop1.Subprop2.Targetprop.
PS: English is not my first language, sorry for your eyes.
1) The answer is yes.
For exemple if you want to define a template in your window for a simple string
public MainWindow()
{
InitializeComponent();
DataTemplate template = new DataTemplate(typeof(string));
FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
borderFactory.SetValue(Border.PaddingProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderBrushProperty, Brushes.Red);
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, new Binding
{
Mode = BindingMode.OneWay
});
borderFactory.AppendChild(textFactory);
template.VisualTree = borderFactory;
myControl.ContentTemplate = template;
}
And in the Window you just put something like
<ContentControl x:Name="myControl" Content="Test text" Margin="10"/>
Your content control will render the string surrounded by a red border.
But as you anc see it is really complex to define your templates in this way.
The only scenario where i could imagine this approache is for some kind of procedurally generated templates.
Another way is to generate a string for the template and then load it with XamlReader:
string xaml = "<Ellipse Name=\"EllipseAdded\" Width=\"300.5\" Height=\"200\"
Fill=\"Red\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>";
object ellipse = XamlReader.Load(xaml);
2) I don't really see the need to generate templates in code behind. For exemple for this kind of data structure:
public class User
{
public string Name { get; set; }
public User Friend { get; set; }
}
public class RootNode
{
public string Title { get; set; }
public User User { get; set; }
public List<Node> Nodes { get; set; }
}
public class Node
{
public string Title { get; set; }
public List<SubNode> SubNodes { get; set; }
}
public class SubNode
{
public string Title { get; set; }
}
You can define this type of template:
<Window
...
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:RootNode}" ItemsSource="{Binding Nodes}">
<StackPanel x:Name="spContainer" Orientation="Horizontal">
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding User}" Value="{x:Null}">
<Setter TargetName="spContainer" Property="Background" Value="Yellow"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding SubNodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:SubNode}" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding RootNodes}"/>
</Grid>
</Window>
As you can see you can define a template by data type, you can also use triggers to modify the behavior in specific cases, you could also use som binding converters...
3) You can bind to nested properties just like to normal ones :
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
However in some cases more than two level bindings could fail (fail to resolve or fail to update when property changes, ...)
I am trying to bind a listbox via ItemsTemplate to a collection of custom "Document" objects but am having an issue while trying to bind an image to the Document.ImageResourcePath property. Here is my markup
<ListBox Name="lbDocuments">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Path=ImageResourcePath}"
Margin="5,0,5,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is my load event for the form that has the listbox.
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
List<Objects.Document> docs = Objects.Document.FetchDocuments();
lbDocuments.ItemsSource = docs;
}
My Document class holds a string to a resource image located in my resources folder depending on the document extension.
e.g. (this is part of a case statement within the document class)
case Cache.DocumentType.Pdf:
this.ImageResourcePath = "/JuvenileOrganizationEmail;component/Resources/pdf_icon.jpg";
break;
When the Window loads I get absolutely nothing in my listbox when it is bound to 23 perfectly well Document types. What could I be doing wrong?
Use an ObservableCollection instead of a List, and make the reference "class level" to your Window.
ObservableCollection<Objects.Document> _docs;
Make sure the DataContext is set in the Window's Ctor.
public Window()
{
_docs = new ObservableCollection<Objects.Document>(Objects.Document.FetchDocuments());
this.DataContext = this;
}
Then, you can just update your Window Loaded event:
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
lbDocuments.ItemsSource = _docs;
}
Or, an alternative solution, will be binding the ItemsSource of the ListBox directly to a public property of the collection. This is assuming the Ctor (above) is still used.
<ListBox Name="lbDocuments" ItemsSource={Binding Docs}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Path=ImageResourcePath}" Margin="5,0,5,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In your Window.cpp file (though, a separate ViewModel class may be recommended if you are doing MVVM)
public ObservableCollection<Objects.Document> Docs
{
get { return _docs; }
}
Is there a convention when using MVVM to bind the items of a ListBox to a ViewModel?
In the below XAML, I'm creating a ListBox of buttons. The ListBox is bound to an observable collection from my ViewModel. I then want to bind the button's Command property to an ICommand. The problem is that when I add that binding, I'm binding against the data object, not the ViewModel.
Do I just change the MyListOfDataObjects property to be a list of ViewModels? If so, where do I instantiate those new objects? I'd prefer to use dependency injection since they will have several dependencies. Do I change the GetData lambda?
In general: what's considered good practice here? I wasn't able to find any examples for this situation, although I assume it is rather common.
I'm using the MVVMLight framework, but I'm willing to look at any other frameworks.
<Window x:Class="KeyMaster.MainWindow"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate">
<Button Command="{Binding ButtonPressedCommand}"
CommandParameter="{Binding .}"
Content="{Binding Name}" />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ListBox ItemsSource="{Binding MyListOfDataObjects}"
ItemTemplate="{StaticResource MyDataTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
</Grid>
</Window>
I'm using the standard MVVMLight ViewModel:
using GalaSoft.MvvmLight;
using KeyMaster.Model;
using System.Collections.ObjectModel;
namespace KeyMaster.ViewModel
{
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private ObservableCollection<MyData> _myListOfDataObjects;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
return;
}
MyListOfDataObjects = new ObservableCollection<MyData>(item);
});
}
public ObservableCollection<MyData> MyListOfDataObjects
{
get { return _myListOfDataObjects; }
set
{
if (_myListOfDataObjects == value) return;
_myListOfDataObjects = value;
RaisePropertyChanged(() => MyListOfDataObjects);
}
}
}
}
Thanks.
In MVVM, there is a clear seperation between the raw data (also known as the Model) and the ViewModel. The ViewModel is the one who is in charge of parsing the data and even modifying it to whatever form it wishes, before passing it to the View.
A simple example is having the Model as XML and having the ViewModel parse it, take only a specific property (for example a "Name") from each element and add them to a list. Only this list will be shown in the View.
That said, I guess you can see where I'm going - the Command should be in the ViewModel not in the Model. As you stated by yourself, you should keep as much of the UI logic out of both the VM and the Model.
If you have a specific command that does something specific on a certain type of data, you can want it in a more "general" type of ViewModel, you can use the CanExectue to only allow this command in specific cases. But still, the command should sit in the ViewModel.
In your specific case, I don't see a problem having the command in the ViewModel, and when raised it will do whatever you need on your data. You don't need a list of ViewModels, you need only one.
I would say it'd depend where you want the functionality of the button-press. If it is always related to the MyData object then (if possible) would it be so out of place to put the Command in the MyData object? (ps. I wouldn't call your MyData object ViewModels just because you're adding a command property to them, as they're not associated with a view)
Alternatively if you want the command in the VM then you could try bind the command using the datacontext of the window. ie something like;
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ButtonPressedCommand}"
CommandParameter="{Binding .}"
Content="{Binding Name}" />
Although I've had trouble in the past with that and went with adding the command to the individual objects.
I'm new to WPF and before I dive in solving a problem in completely the wrong way I was wondering if WPF is clever enough to handle something for me.
Imagine I have a collection containing objects. Each object is of the same known type and has two parameters. Name (a string) and Picked (a boolean).
The collection will be populated at run time.
I would like to build up a UI element at run time that will represent this collection as a series of checkboxes. I want the Picked parameter of any given object in the collection updated if the user changes the selected state of the checkbox.
To me, the answer is simple. I iterate accross the collection and create a new checkbox for each object, dynamically wiring up a ValueChanged event to capture when Picked should be changed.
It has occured to me, however, that I may be able to harness some unknown feature of WPF to do this better (or "properly"). For example, could data binding be employed here?
I would be very interested in anyone's thoughts.
Thanks,
E
FootNote: The structure of the collection can be changed completely to better fit any chosen solution but ultimately I will always start from, and end with, some list of string and boolean pairs.
I would strongly recommend the ItemsControl, its behaviour is as close as you can get to the ASP.Net repeater control so it is very flexible.
Declare the item control as:
<ItemsControl Name="YourItemsControl"
ItemsSource="{Binding Path=YourCollection}"
ItemTemplate="{StaticResource YourTemplate}">
</ItemsControl>
Then you can use the datatemplate to organise the data into a display format for the user
<DataTemplate x:Key="ProjectsTemplate">
<StackPanel Margin="0,0,0,10">
<Border CornerRadius="2,2,0,0" Background="{StaticResource ItemGradient}" d:LayoutOverrides="Width, Height">
<local:ItemContentsUserControl Height="30"/>
</Border>
...
Useful ItemsControl Links
http://drwpf.com/blog/itemscontrol-a-to-z/
http://www.galasoft.ch/mydotnet/articles/article-2007041201.aspx
I hope this helps you.
You can use Data Templates. Here's a good post about it.
This is exactly the kind of scenario WPF simplifies. Event-handlers- bah! Data-binding and data templates make this a cinch. I have constructed an example illustrating how you can do this.
Here is the code-behind, which declares a class to represent your items- PickedItem. I then create a collection of these items and populate it with some samples.
public partial class DataBoundCollection : Window
{
public DataBoundCollection()
{
Items = new ObservableCollection<PickedItem>();
Items.Add(new PickedItem("Item 1"));
Items.Add(new PickedItem("Item 2"));
Items.Add(new PickedItem("Item 3"));
InitializeComponent();
}
public ObservableCollection<PickedItem> Items
{
get;
set;
}
}
public class PickedItem
{
public PickedItem(string name)
{
Name = name;
Picked = false;
}
public string Name
{
get;
set;
}
public bool Picked
{
get;
set;
}
}
Now, let's look at the XAML mark-up for this window:
<Window x:Class="TestWpfApplication.DataBoundCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBoundCollection" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Picked}" Margin="5"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I create a ListBox to hold the items, and bind its ItemsSource property to the collection I created in the code-behind. Then, I provide the ListBox with an ItemTemplate, which determines how each PickedItem will be rendered. The DataTemplate in this case is as simple as a check-box and some text, both bound to the member variables on PickedItem. Now, when I check any of these items, the data in the underlying collection is modified, in real-time, with no event handlers needed. Ta-da!
alt text http://img405.imageshack.us/img405/1083/databoundcollection.png
Problem
I've got a collection of IThings and I'd like to create a HierarchicalDataTemplate for a TreeView. The straightforward DataType={x:Type local:IThing} of course doesn't work, probably because the WPF creators didn't want to handle the possible ambiguities.
Since this should handle IThings from different sources at the same time, referencing the implementing class is out of question.
Current solution
For now I'm using a ViewModel which proxies IThing through a concrete implementation:
public interface IThing {
string SomeString { get; }
ObservableCollection<IThing> SomeThings { get; }
// many more stuff
}
public class IThingViewModel
{
public IThing Thing { get; }
public IThingViewModel(IThing it) { this.Thing = it; }
}
<!-- is never applied -->
<HierarchicalDataTemplate DataType="{x:Type local:IThing}">
<!-- is applied, but looks strange -->
<HierarchicalDataTemplate
DataType="{x:Type local:IThingViewModel}"
ItemsSource="{Binding Thing.SomeThings}">
<TextBox Text="{Binding Thing.SomeString}"/>
</HierarchicalDataTemplate>
Question
Is there a better (i.e. no proxy) way?
Another alternative (similar to jing's solution): If you only have one type of item, you can set the ItemTemplate directly. Then you don't need to set a key or a datatype.
In your ViewModel:
public ObservableCollection<IThing> Thingies { get; private set; }
In the View:
<TreeView ItemsSource="{Binding Thingies}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SomeThings}">
<TextBox Text="{Binding SomeString}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The reason for this is that the default template selector supports only concrete types, not interfaces. You need to create a custom DataTemplateSelector and apply it to the ItemTemplateSelector property of the TreeView. I can't find the URL where I found an example of it, but hopefully with this info, you can Google it.
Another solution is you give a key to the HierarchicalDataTemplate and put it in the Windows.Resources, and manually reference to it in the TreeView. <TreeView ItemDataTemplate={StaticResource templateKey}/>
But that limits the autoselection of data template according to data type, which is provided by WPF TreeView.