I'm trying to bind to a collection of controls I generate dynamically:
<ItemsControl ItemsSource="{Binding CustomFields}">
And the code:
public ObservableCollection<Control> CustomFields
{
get
{
return GetCustomFields();
}
}
The Getcustomfields just generates some controls like a ComboBox, textbox etc. The binding seems to work, but the window doesn't show any of my controls. Probably because I need an datatemplate in the itemscontrol. My question is what kind of datatemplate do I need?
Thanks for any help
The following property like with the same XAML as you use:
public ObservableCollection<UIElement> Controls
{
get
{
var collection = new ObservableCollection<UIElement>();
collection.Add(new Button { Content = "Hello world" });
return collection;
}
}
Maybe your problem comes from somewhere else... Can you give us the code needed to reproduce the problem ?
Related
I'm having trouble binding XmlDataProvider from ViewModel (my DataContext) to ListBox's ItemsSource.
This is what I did -
In Xaml -
<ListBox ItemsSource="{Binding Path=SelectedXmlProvider}">...</ListBox>
In my ViewModel class -
public XmlDataProvider SelectedFeedXmlProvider
{
get { return _selectedFeedXmlProvider; }
set
{
_selectedFeedXmlProvider = value;
RaisePropertyChanged("SelectedFeedXmlProvider");
}
}
In my application -
viewModel.SelectedFeedXmlProvider = new XmlDataProvider
{
Source = new Uri("http://sxp.microsoft.com/feeds/3.0/MSDNTN/CSharpHeadlines"),
XPath = "//item"
};
And... ListBox is EMPTY! (where it should have been full with data).
When setting the ListBox's ItemsSource to an XmlDataProvider that's defined as a static resource, all is fine. Thing is, I must use MVVM, and I need to be able to dynamically change the XML source.
Can someone help? what's wrong with the code above? why is the list empty?
Thanks!
What I'm trying to achieve is:
Have a ListView bound to an ObservableCollection of ItemRecords.
Have a TabControl that contains detailed view for all the ItemRecords in the ListView that were selected for editing.
Each TabItem contains a UserControl ("ItemInfo") that uses ItemInfoViewModel as its VM (and, not so coincidentally, DataContext).
ItemInfo UserControl needs to be populated with the data from the corresponding ItemRecord.
To achieve that, I'm trying to pass the ItemRecord (selected in the ListView) to ItemInfoViewModel.
Finally, the question: what do you think would be the best way to do this, without breaking the MVVM pattern?
The not-so-elegant way that I see (and it actually doesn't exactly follow the MVVM principles) is to have a DependencyProperty ItemRecord defined in the UserControl, provide its value via binding, and in the constructor (in the UserControl's code-behind) pass the ItemRecord to the VM (which we get by casting the DataContext).
The other problem is with how to actually pass the ItemRecord via binding.
Once I set the VM as the UserControl's DataContext, I cannot just use {Binding} to specify the current item in TabControl's source collection.
At the moment I am binding to the TabControl's SelectedItem using ElementName - but this doesn't sound too robust :-(
<localControls:TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer>
<localControls:ItemInfo ItemRecord="{Binding ElementName=Tabs, Path=SelectedItem}"/>
</ScrollViewer>
</DataTemplate>
</localControls:TabControl.ContentTemplate>
Any good advice will be greatly appreciated!
Alex
I think your problem is you're not quite understanding the MVVM pattern here; you're still looking at this as the different controls talking to each other. Where in MVVM, they should not be, each control is communicating with the view model independently of all the others. And the view model controls (and supplies) the logic which tells the controls how to behave.
So, ideally you would have something like:
public ObservableCollection<ItemRecord> ListViewRecords
{
get { ... }
set { ... }
}
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set { ... }
}
The ListViewRecords would be bound to the ItemsSource property of your ListView (the actual properties might vary based on the specific controls you're using, I'm used to the Telerik suite at the moment so that's where my head is). And the SelectedListViewRecords would be bound to the SelectedItems property of the ListView. Then for your TabControl you would have:
public ObservableCollection<MyTabItem> Tabs
{
get { ... }
set { ... }
}
public MyTabItem SelectedTab
{
get { ... }
set { ... }
}
Again, you would bind the Items property to the Tabs and SelectedItem to the SelectedTab on your TabControl. Now your view model contains all the logic, so in your SelectedListViewRecords you might do something like this:
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set
{
_selectedRecords = value;
NotifyPropertyChanged("SelectedListViewRecords");
Tabs.Clear(); // Clear the existing tabs
// Create a new tab for each newly selected record
foreach(ItemRecord record in value)
Tabs.Add(new MyTabItem(record));
}
}
So the idea here is that the controls do nothing more than send and receive property changes, they know nothing of the underlying data, logic, etc. They simply show what their bound properties tell them to show.
What would be the best way to get the elements of a combobox to each support a Command and CommandParameter?
I'd like to implement the Theme Chooser shown toward the bottom of this blog post, except with a combo box instead of a context menu. I'd need each element of the combobox to support a Command and CommandParameter, and I'd like it to just be plain text, as the combo below is.
<ComboBox>
<ComboBox.Items>
<TextBlock>A</TextBlock>
<TextBlock>B</TextBlock>
<TextBlock>C</TextBlock>
</ComboBox.Items>
</ComboBox>
I tried hyperlinks, but the main problem there is that when you click directly onto the link text, the combo box does not close.
Is there an easy way to do this?
EDIT
Ok, well the specific goal that I said I wanted to achieve—having a combo change the SL Toolkit theme—is trivially accomplished. I can simply bind the selected item of the combo to a ViewModel property that then exposes the appropriate themeuri which my SL Toolkit theme can bind to, or, since this is purely a UI activity with no business logic, I can just catch the combobox item changed event, and update my themeUri from there.
I am curious though, is there a good way to bind each combo box item to a command with a command parameter? Using a Hyperlink as each comboboxItem seemed promising, but that prevents the CB from closing after you click on an item when you click the actual hyperlink.
You could Bind the selected item to your ViewModel and then the setter would trigger when the Theme was changed.
Xaml:
<ComboBox SelectedItem="{Binding SelectedTheme, Mode=TwoWay}" ItemsSource="{Binding Themes}" />
CodeBehind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new MainPageViewModel();
}
}
ViewModel:
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> Themes { get; set; }
private string _selectedTheme;
public string SelectedTheme
{
get { return _selectedTheme; }
set
{
_selectedTheme = value;
// Change the Theme
RaisePropertyChanged("SelectedTheme");
}
}
public MainPageViewModel()
{
Themes = new ObservableCollection<string>();
Themes.Add("Red");
Themes.Add("Green");
Themes.Add("Blue");
}
}
Just looking for some input as to what control I should go with or a broad approach. I am going to load up a png in the program I am writing. Then I could specify that I want 32x32 lines split over the picture (I'm not breaking the picture up, just specifying a grid to be on top). So, obviously I am going to need something which I can select multiples of these "cells" (which the grid or whatever broke into) and easily identify which the user is selecting. Does the grid do this or is it something more like creating guidelines and then creating some rectangles or something?
You would use an ItemsControl or derived class such as the Selector with the ItemsPanel property set to a Grid. In the ItemsContainerStyle property would set the Style for a ContentControl. The ContentControl is a the of object that will be generated for each item in the list that your ItemsControl will be bound against using the ItemsSource property. In that style you will setup a ControlTemplate for the ContentControlto soemthing that includes a Border or Rectangle or similar to get the grid lines. The root Control in your ControlTempalte will have the Grid.Row and Grid.Column properties bound against the .Row and .Column properties of your dataitems wich will be the DataContext.
Finally you bind the ItemsControl agains an ObservableCollection of these DataItems.
<ItemsControl ItemsSource={Binding MyDataItems} ....
Your DataItem would look something like this:
public class DataItem : INotifyPropertyChanged
{
public int Row
{
get { // return field }
set { // raise the PropertyChanged event here }
}
public int Column
{
get { // return field }
set { // raise the PropertyChanged event here }
}
}
I have a form that binds to an ViewModel (MVVM). Inside the form I have an ItemsControl that is bound to an element called projects in my DataContext.
When I do a save using a command pattern I save the item and do a retrieve then I want to rebind the ItemsControl to the Projects collection. This part doesn't seem to be working, all my service calls work as expected but my view is not rebound to the new collection with the added item even though it gets returned from the server.
Any help with this would really be appreciated.
XAML
<ItemsControl Name="ProjectGrid"
Background="Transparent" ItemsSource="{Binding Path=Projects}" Margin="0,0,0,0" VerticalAlignment="Top"
ItemContainerStyle="{StaticResource alternatingWithTriggers}"
AlternationCount="2"
ItemTemplate="{StaticResource ItemTemplate}"/>
ViewModel
public ICommand SaveCommand
{
get
{
if (_cmdSave == null)
{
_cmdSave = new RelayCommand(Save, CanSave);
}
return _cmdSave;
}
}
public void Save()
{
MyService.Save();
PopulateModel();
}
private void PopulateModel()
{
Projects = MyService.GetProjects();
}
public ProjectDto[] Projects
{
get { return _projects; }
set
{
if (_projects == value)
return;
_projects = value;
Notify(PropertyChanged, o => Projects);
}
}
Make sure your ViewModel is implementing INotifyPropertyChanged. Your ui wont know about the change if your view model doesnt inform it when the property changes
use a debug converter to ascertain if your binding is failing. there is an example here of how to do this. This is a technique every wpf developer needs.
im pretty sure its your NotifyPropertyChanged that is failing, the debug converter will tell you for certain
As Aran Mulholland already said, implement INotifyPropertyChanged in your ViewModel.
Also, try to use an ObservableCollection for your collections.
And instead of resetting the collection, try to use
Projects.Clear();
MyService.GetProjects().ToList().ForEach(Projects.Add);
And just as a tip, try to make the GetProjects() method async, so it won't block the UI...