Im trying to supply a data template to my ribbon.
The ribbon is declared as following, and has an ItemTemplate attached to it.
<r:Ribbon Name="RibbonMain"
ItemTemplate="{StaticResource HomeRibbonTabTemplate}">
</r:Ribbon>
The Datatemplate is the following:
<Window.Resources>
<DataTemplate DataType="{x:Type local:RibbonContainer}"
x:Key="HomeRibbonTabTemplate">
<r:RibbonTab Header="{Binding Path=HeaderName}">
<r:RibbonGroup Header="{Binding Path=GroupName}">
</r:RibbonGroup>
</r:RibbonTab>
</DataTemplate>
</Window.Resources>
I do then attach the ItemsSource:
public MainWindow()
{
InitializeComponent();
var RibbonTabData = new ObservableCollection<RibbonContainer>();
RibbonTabData.Add(new RibbonContainer("HeaderName", "GroupName"));
RibbonMain.ItemsSource = RibbonTabData;
}
Lastly the class: (Which just contains two string fields)
class RibbonContainer
{
public string HeaderName
{
get;
set;
}
public string GroupName
{
get;
set;
}
public RibbonContainer(string _headername, string _groupname)
{
HeaderName = _headername;
GroupName = _groupname;
}
}
I get the unimpressive result of showing the fully qualified class name in the tab header and neither is the ribbongroup showing. (This is what the datatemplate should solve?)
What to do?
Best regards
I'm not exactly sure where to start, but perhaps with a short warning that, in trying to create a RibbonControl totally from data Binding and data items, you really are opening up a huge can of whoop ass on yourself. This is because the developers that designed the code for it used unconventional patterns for some of it and failed to adequately document how to do things with it. Some of the best sources will be found by searching on this website.
So anyway, if you're up for a painful, uphill struggle, read on. Your first mistake was trying to use a DataTemplate for the RibbonTab because it is extends System.Windows.Controls.ItemsControl and therefore requires a HierarchicalDataTemplate. Your second mistake was declaring the RibbonTab inside the template, as #devhedgehog mentioned in a comment.
You third mistake was setting the x:Key value for your DataTemplate and applying it to the Ribbon.ItemsTemplate property... I know, I know... a sensible enough thing to do if this wasn't a RibbonControl. You'll have to ask those developers as to why that doesn't work, but you're better off just accepting that it doesn't and adapting your code. You just need to remove the x:Key value and the Ribbon.ItemsTemplate property and let the Framework apply the template implicitly.
Now if you ever want more than one RibbonGroup, then your fourth mistake was defining that in the template for the RibbonTab. If you're going to do this properly, then your data classes will need to match the various levels of UI elements in the Ribbon. By this, I mean that you need to create a RibbonGroupData class too. That class needs a collection of RibbonButtonData objects that supply the data to each RibbonButton in the UI. So you should end up with something like this:
public class RibbonTabData : BaseDataType
{
private string name = string.Empty;
private ObservableCollection<RibbonGroupData> ribbonGroupData = new ObservableCollection<RibbonGroupData>();
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
public ObservableCollection<RibbonGroupData> RibbonGroupData
{
get { return ribbonGroupData; }
set { ribbonGroupData = value; NotifyPropertyChanged("RibbonGroupData"); }
}
}
public class RibbonGroupData : BaseDataType
{
private string name = string.Empty;
private ObservableCollection<RibbonButtonData> ribbonButtonData = new ObservableCollection<RibbonButtonData>();
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
public ObservableCollection<RibbonButtonData> RibbonButtonData
{
get { return ribbonButtonData; }
set { ribbonButtonData = value; NotifyPropertyChanged("RibbonButtonData"); }
}
}
public class RibbonButtonData : BaseDataType
{
private string name = string.Empty;
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
}
The BaseDataType class just implements the INotifyPropertyChanged interface. Of course, you'd need to add extra properties for ICommands and image sources, etc. You might even need different RibbonButtonData classes with different properties for different types of RibbonButtons and then you'd need a common RibbonButtonBaseData class that they all extended, so your collection could contain all the different types together. So there's lots more for you to do, but given this example code, you could display it in the Ribbon like this:
<Ribbon:RibbonWindow.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataTypes:RibbonTabData}"
ItemsSource="{Binding RibbonGroupData}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type DataTypes:RibbonButtonData}">
<Ribbon:RibbonButton Label="{Binding Name}"
LargeImageSource="/WpfRibbonApplication1;component/Images/LargeIcon.png" />
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataTypes:RibbonGroupData}"
ItemsSource="{Binding RibbonButtonData}">
<Ribbon:RibbonGroup Header="{Binding Name}" />
</HierarchicalDataTemplate>
</Ribbon:RibbonWindow.Resources>
<Ribbon:Ribbon x:Name="Ribbon" ItemsSource="{Binding RibbonTabData}" />
Now in the view model that is set as the DataContext for the Window, I can add some dummy data to test that it all works:
RibbonTabData.Add(new RibbonTabData() { Name = "Tab 1", RibbonGroupData = new ObservableCollection<RibbonGroupData>() { new RibbonGroupData() { Name = "Group 1", RibbonButtonData = new ObservableCollection<RibbonButtonData>() { new RibbonButtonData() { Name = "Button 1" }, new RibbonButtonData() { Name = "Button 2" }, new RibbonButtonData() { Name = "Button 3" } } }, new RibbonGroupData() { Name = "Group 2", RibbonButtonData = new ObservableCollection<RibbonButtonData>() { new RibbonButtonData() { Name = "Button 1" }, new RibbonButtonData() { Name = "Button 2" } } } } });
RibbonTabData.Add(new RibbonTabData() { Name = "Tab 2" });
RibbonTabData.Add(new RibbonTabData() { Name = "Tab 3" });
And we get this:
However, even with this helpful start, you've still got a lot more work to do.
By reading Sheridans answer I managed to create the following result:
(Different controls with the possibility to attach event handlers to wanted control).
How I did the event handling (example with ribbonbutton)
Attach a tag property to your ribbonbutton template (with databinding of course)
Attach an loaded event into your ribbonbutton template
Create a dictionary: (in your windowname.xaml.cs
public Dictionary<string, List<RoutedEventHandler>> EventLibrary = new Dictionary<string, List<RoutedEventHandler>>();
Add an event to dictionary and extend the string with type of event
EventLibrary.Add("NAME_RIBBONBUTTON_CLICKEVENT", new List<RoutedEventHandler> { new RoutedEventHandler(RibbonButton_Test)});
This is the event loaded code:
private void RibbonButton_Loaded(object sender, EventArgs e)
{
System.Windows.Controls.Ribbon.RibbonButton cmd = (System.Windows.Controls.Ribbon.RibbonButton)sender;
if (EventLibrary.ContainsKey(cmd.Tag.ToString() + "_CLICKEVENT"))
{
List<RoutedEventHandler> value = EventLibrary[cmd.Tag.ToString() + "_CLICKEVENT"];
for (int i = 0; i < value.Count; i++)
{
cmd.AddHandler(RibbonButton.ClickEvent, value[i]);
}
}
}
Here is a link to old post in WPF blog, there you can download archive with solution, where you can find couple of useful things:
ViewModels for all Ribbon controls
Styles with all appropriate bindings
So at the end using above things I receive more simple solution:
<HierarchicalDataTemplate DataType="{x:Type ribbonVM:RibbonTabVM}" ItemsSource="{Binding Groups}">
<RibbonTab DataContext="{Binding}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type ribbonVM:RibbonGroupVM}" ItemsSource="{Binding Controls}">
<RibbonGroup DataContext="{Binding}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ribbonVM:RibbonButtonVM}">
<RibbonButton DataContext="{Binding}" />
</DataTemplate>
The only thing I added to VMs were collections of child elements.
Related
In my ViewModel I have a BindingList which holds my Views that should be displayed as tabs in my TabControl. The text for the tab is defined in the code behind of the view. I also defined a simple test class to test the binding which works perfectly. Only the binding to the code behind property does not work.
Xaml code for my TabControl:
<TabControl ItemsSource="{Binding TabControlContentList}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName, FallbackValue=FallbackValue}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
The BindingList which it is bound to:
void RaisePropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public BindingList<object> TabControlContentList
{
get => tabControlContentList;
set
{
tabControlContentList = value;
RaisePropertyChanged();
}
}
private BindingList<object> tabControlContentList = new BindingList<object>();
Test class:
class ControlTest
{
public string DisplayName { get; private set; } = "";
public ControlTest(string name) => DisplayName = name;
}
Xaml code behind of AnalysisView.xaml.cs:
public partial class AnalysisView
{
public string DisplayName { get; private set; } = "Analysis";
public AnalysisView()
{
InitializeComponent();
}
}
In my ViewModel I have the following code:
public AnalysisView analysisView = new AnalysisView();
TabControlContentList.Clear();
TabControlContentList.Add(new ControlTest("Menu 1"));
TabControlContentList.Add(new ControlTest("Menu 2"));
TabControlContentList.Add(analysisView);
TabControlContentList.Add(new ControlTest("Menu 3"));
TabControlContentList.Add(new ControlTest("Menu 4"));
My TabControl then shows five tabs. First two have the text "Menu 1" and "Menu 2" on them, the third one reads "FallbackValue", followed by "Menu 3" and "Menu 4".
I have no idea anymore why the binding on the property in code behind in AnalysisView.xaml.cs does not work. Is this maybe a general Wpf thing?
Before I present my answer, I highly recommend that you download and learn how to use Snoop for WPF, or use the Visual Studio built-in XAML runtime inspection tools. These allow you to look at bindings, data context, etc.
The main reason why your code doesn't work, is that a UserControl doesn't have a DataContext by default.
So, updating your AnalysisView like so will give it a DataContext pointing to itself:
public AnalysisView()
{
InitializeComponent();
DataContext = this;
}
However, the DataContext in a UserControl won't flow into your DataTemplate like your other objects. To make it work, you need to change your binding like so:
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.DisplayName, FallbackValue=ERROR!, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" />
</DataTemplate>
Working with UserControls like this feels like a hack. So you might want to look for ways to design whatever app you're building a little differently.
Also, the code in your sample is not using INotifyPropertyChanged for properties, so you won't be getting any change notifications. You might want to learn more about the MVVM pattern.
I have a menu and some submenus
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "Select a Building",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "Building 4",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel { Header = "< 500",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel {Header = "Executives" },
new MenuItemViewModel {Header = "Engineers" },
new MenuItemViewModel {Header = "Sales" },
new MenuItemViewModel {Header = "Marketing"},
new MenuItemViewModel {Header = "Support"}
}
},
new MenuItemViewModel { Header = "500 - 999",
MenuItems = new ObservableCollection<MenuItemViewModel>
{
new MenuItemViewModel {Header = "Executives" },
new MenuItemViewModel {Header = "Engineers" },
new MenuItemViewModel {Header = "Sales" },
new MenuItemViewModel {Header = "Marketing"},
new MenuItemViewModel {Header = "Support"}
}
}
}
}
}
I am trying to capture the value of each selection the user makes and display them in a listbox. For example a user selects "Building 4" then "500 - 999" then "support" as those values are selected they populated the list box. I have a function in MenuItemViewModel that is called Execute(), this will get the Header of the last value selected, I.e "support" but I cannot figure out how to get that value to the listbox.
Here is the ViewModel
public class MenuItemViewModel
{
private readonly ICommand _command;
string Value;
public ObservableCollection<Cafe> Cafes
{
get;
set;
}
public MenuItemViewModel()
{
_command = new CommandViewModel(Execute);
}
public string Header { get; set; }
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get
{
return _command;
}
}
public void Execute()
{
MessageBox.Show("Clicked at " + Header);
}
}
And finally the xaml for the MenuItem and ListBox:
<Menu x:Name="buildingMenu" Margin="0,15,0,0" HorizontalAlignment="Left" Height="20" Width="200" ItemsSource="{Binding MenuItems}" >
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=MenuItems}">
<TextBlock Text="{Binding Header}"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<ListBox Name="selectionListBox" HorizontalAlignment="Right" Height="179" Margin="0,177,55,0" VerticalAlignment="Top" Width="500" />
I have tried to add the Header to a List in the ViewModel but I cannot get anything work. Is there something similar to a combobox's SelectedValue that can be used here? Thanks for the help
This is actually a lot harder to do than you might expect, because you're using menus in a way they're not really designed to be used.
First of all you're going to need a list of items in your view model to bind to, eg:
public ObservableCollection<string> ListItems { get; set; }
And the corresponding binding in your ListBox:
<ListBox ItemsSource="{Binding ListItems}" Name="selectionListBox" />
Next, you'll need to populate this list with items as they're being clicked. This is tricky with the way you've done things at the moment because ListItems will need to be in your main view model, yet your command handler is in MenuItemViewModel. This means you'll either need to add a way for MenuItemViewModel to call it's parent, or better yet move the command handler to the main view model (so that it's now shared) and have the MenuItems pass in their data context object. I use MVVM Lite and in that framework you do something like this:
private ICommand _Command;
public ICommand Command
{
get { return (_Command = (_Command ?? new RelayCommand<MenuItemViewModel>(Execute))); }
}
public void Execute(MenuItemViewModel menuItem)
{
// ... do something here...
}
You don't seem to be using MVVM Lite, but whatever your framework it is it will have support for passing a parameter into your execute function, so I'll leave that for you to look up. Either way your MenuItem Command bindings will now all need to be modified to point to this common handler:
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding ElementName=buildingMenu, Path=DataContext.Command}" />
</Style>
Your other requirement is for the list to populate as each submenu item is selected, and this is where things get nasty. Submenus don't trigger commands when they close, they trigger SubmenuOpened and SubmenuClosed events. Events can't bind to the command handlers in your view model, so you have to use code-behind handlers instead. If you were declaring your MenuItems explicitly in XAML you could just set them directly there, but your Menu is binding to a collection, so your MenuItems are being populated by the framework and you'll have to set the handler by adding an EventSetter to your style:
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="SubmenuOpened" Handler="OnSubMenuOpened" />
<Setter Property="Command" Value="{Binding ElementName=buildingMenu, Path=DataContext.Command}" />
</Style>
This works, in that calls the specified handler function in your MainWindow code-behind:
private void OnSubmenuOpened(object sender, RoutedEventArgs e)
{
// uh oh, what now?
}
The problem of course is that event handlers exist in the view, but in MVVM we need it in the view model. If you're happy to corrupt your views like this for the sake of getting your application to work then check this SO question for some example code showing how to get it working. However, if you're a hard-core MVVM purist like myself you'll probably want a solution that doesn't involve code-behind, and in that case you'll again need to refer to your framework. MVVM Lite provides a behavior that allows you to convert events to command, you typically use it like this:
<MenuItem>
<i:Interaction.Triggers xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:EventTrigger EventName="OnSubmenuOpened">
<cmd:EventToCommand Command="{Binding SubmenuOpenedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
The problem with behaviours though is that you can't set them in a style. Styles are applied to all objects, which is why EventSetter works fine. Behaviours have to be created for each object that they're used for.
So the final piece of the puzzle is that if you have cases in MVVM where you need to set a behaviour in a style then you'll need to use the solution posted by vspivak in his article Using System.Windows.Interactivity Behaviors and Actions in WPF/Silverlight styles. I've used this myself in commercial projects, and it's a nice general solution to the bigger problem of redirecting event handlers to command handlers in styles.
Hope that answers your question. I'm sure it seems ridiculously convoluted, but what you're trying to do is a fairly pathological scenario well outside how WPF is typically used.
Put a Click event on any MenuItem that is created and when the event is fired, acquire the data item off of the source's DataContext which seeded the MenuItem.
Example
I created a recent list of operation which were needed as submenu's under a Recent menu. I seeded the sub list as a model of typeMRU which was used to load the menu *via ItemsSource and held in an ObservableCollection<MRU>() MRUS :
Xaml
<MenuItem Header="Recent"
Click="SelectMRU"
ItemsSource="{Binding Path=MRUS}">
...
</MenuItem>
Codebehind
private void SelectMRU(object sender, RoutedEventArgs e)
{
try
{
var mru = (e.OriginalSource as MenuItem).DataContext as MRU;
if (mru is not null)
{
VM.ClearAll();
this.Title = mru.Name;
ShowJSON(File.ReadAllText(mru.Address));
}
}
catch (Exception ex)
{
VM.Error = ex.Demystify().ToString();
}
}
public class MRU
{
public string Name { get; set; }
public string Address { get; set; }
public string Data { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(Name);
public bool IsFile => !string.IsNullOrWhiteSpace(Address);
public bool IsData => !IsFile;
public MRU(string address)
{
if (string.IsNullOrWhiteSpace(address)
|| !File.Exists(address)) return;
Address = address;
Name = System.IO.Path.GetFileName(address);
}
// This will be displayed as a selectable menu item.
public override string ToString() => Name;
}
Note that the full Menu code can be viewed in public gist Gradient Menu Example with MRU
I have a TabControl with multiple DataTemplate. the first DataTemplate will be used for search reasons and the second will be for displaying items obtained from that search. My XAML code will be as follows:
<UserControl.Resources>
<!--First template-->
<DataTemplate>
<!-- I will have a DataGrid here-->
</DataTemplate>
<!--Second template-->
<DataTemplate >
<!-- I will have details of one item of the DataGrid-->
</DataTemplate>
</UserControl.Resources>
<TabControl ItemsSource="{Binding }"/>
What I want to accomplish is that in the TabControl the first tab will contain the first DataTemplate (the search template) and when I double click on one row of my DataGrid, a tab will be added with the details of that row (in other words a tab with the second template).
Since I am using MVVM, I thought of creating two UserControls, one for each template and then catch the double click event, but after this I don't know how to add a tab since now my search template is a UserControl seperated from the one that contains the TabControl.
So how do I do this?
UPDATE:
As I read the answers I think I wasn't very clear in stating the problem.
My problem is how to add tabs with the second template, by catching double click events from the first template. I don't have any problem in adding the two templates independently.
Rather can creating two UserControls, you can create and use a DataTemplateSelector in order to switch different DataTemplates in.
Basically, create a new class that inhereits from DataTemplateSelector and override the SelecteTemplate method. Then declare an instance of it in the XAML (much like a value converter), and then apply it to ContentTemplateSelector property of the TabControl.
More info can be found here.
If you're going to do this with MVVM, your tab control should be bound to some ObservableCollection in your VM, and you just add and remove VM's to the collection as needed.
The VMs can be any type you like and your DataTemplates will show the correct view in the tab just like any other view, so yes, create two UserControls for the two views.
public class MainVM
{
public ObservableCollection<object> Views { get; private set; }
public MainVM()
{
this.Views = new ObservableCollection<object>();
this.Views.Add(new SearchVM(GotResults));
}
private void GotResults(Results results)
{
this.Views.Add(new ResultVM(results));
}
}
There are two options: Use datatemplate selector, or use implicit datatemplates and different types for each tabitem.
1. DataTemplateSelector:
public ObservableCollection<TabItemVM> Tabs { get; private set; }
public MainVM()
{
Tabs = ObservableCollection<TabItemVM>
{
new TabItemVM { Name="Tab 1" },
};
}
void AddTab(){
var newTab = new TabItemVM { Name="Tab 2" };
Tabs.Add(newTab);
//SelectedTab = newTab; //you may bind TabControl.SelectedItemProperty to viewmodel in order to be able to activate the tab from viewmodel
}
public class TabItemTemplateSelector : DataTemplateSelector
{
public DataTemplate Tab1Template { get; set; }
public DataTemplate Tab2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var tabItem = item as TabItemVM;
if (tabItem.Name == "Tab 1") return Tab1Template;
if (tabItem.Name == "Tab 2") return Tab2Template;
return base.SelectTemplate(item, container);
}
}
<local:TabItemTemplateSelector
x:Key="TabItemTemplateSelector"
Tab1Template="{StaticResource Tab1Template}"
Tab2Template="{StaticResource Tab2Template}" />
2. Implicit Data Templates:
public class MainVM : ViewModelBase
{
public ObservableCollection<TabItemVM> Tabs { get; private set; }
public MainVM()
{
Tabs = new ObservableCollection<TabItemVM>
{
new Tab1VM(),
};
}
void AddTab()
{
var newTab = new Tab2VM()
Tabs.Add(newTab);
//SelectedTab = newTab;
}
}
public class TabItemBase
{
public string Name { get; protected set; }
}
public class Tab1VM : TabItemBase
{
public Tab1VM()
{
Name = "Tab 1";
}
}
public class Tab2VM : TabItemBase
{
public Tab2VM()
{
Name = "Tab 2";
}
}
<UserControl.Resources>
<!--First template-->
<DataTemplate DataType="local:Tab1VM">
<!-- I will have a DataGrid here-->
</DataTemplate>
<!--Second template-->
<DataTemplate DataType="local:Tab2VM">
<!-- I will have details of one item of the DataGrid-->
</DataTemplate>
</UserControl.Resources>
I am attmepting to bind an ObservableCollection of items to a ListPicker from the Silverlight toolkit for Windows Phone. I have done this before, but in my case now, my ObservableCollection contains items from a custom class. I do not know how to get each of the properties of the class (for each item) to bind to my ListPicker. To better illustrate what I have is as follows:
MainPage.xaml
<Grid.Resources>
<DataTemplate x:Name="SearchProviderItemTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Favicon}" />
<TextBlock Text="{Binding Name}" Margin="12,0,0,0"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="SearchProviderFullModeItemTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Favicon}" />
<TextBlock Text="{Binding Name}" Margin="12,0,0,0"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<toolkit:ListPicker x:Name="SearchProviderListPicker" ItemsSource="{Binding SearchProvider}" Margin="12,0,12,0"
Header="Search provider" ItemTemplate="{Binding SearchProviderItemTemplate}"
FullModeHeader="Search provider" FullModeItemTemplate="{Binding SearchProviderFullModeItemTemplate}"
SelectedIndex="{Binding}"
SelectionChanged="SearchProviderListPicker_SelectionChanged"
CacheMode="BitmapCache"/>
MainPage.xaml.cs
public MainPage()
{
InitializeComponent();
//This data is placed here for convenience right now
ListItem Bing = new ListItem { Favicon = "", Name = "Bing", Address = "http://www.bing.com/search?q=" };
ListItem Google = new ListItem { Favicon = "", Name = "Google", Address = "http://www.google.com/search?q=" };
ListItem Yahoo = new ListItem { Favicon = "", Name = "Yahoo", Address = "http://search.yahoo.com/search?p=" };
ListItem Ask = new ListItem { Favicon = "", Name = "Ask", Address = "http://www.ask.com/web?q=" };
ListItem Aol = new ListItem { Favicon = "", Name = "AOL", Address = "http://search.aol.com/search?q=" };
Settings.SearchProvider.Value.Add(Bing);
Settings.SearchProvider.Value.Add(Google);
Settings.SearchProvider.Value.Add(Yahoo);
Settings.SearchProvider.Value.Add(Ask);
Settings.SearchProvider.Value.Add(Aol);
// Set the data context of the SearchProviderListPicker control to the data
DataContext = App.ViewModel;
}
MainViewModel.cs
public ObservableCollection<ListItem> SearchProvider { get; private set; }
public MainViewModel()
{
SearchProvider = Settings.SearchProvider.Value;
}
The Settings class referenced above is used to store the SearchProvider ObservableCollection in isolated storage.
Settings.cs
public static Setting<ObservableCollection<ListItem>> SearchProvider = new Setting<ObservableCollection<ListItem>>("SearchProvider", new ObservableCollection<ListItem>());
And the Setting class saves and gets the data from isolated storage.
Also, my custom ListItem class demonstrates the properties I need to use.
ListItem.cs
public string Favicon
{
get;
set;
}
public string Name
{
get;
set;
}
public string Address
{
get;
set;
}
So basically each SearchProvider ObservableCollection item contains the Favicon, Name, and Address that I need to use, and I only want to bind the Favicon and Name to the ListPicker. My problem though, is the ListPicker presents the 5 search providers except the text for each says Project1.Common.ListItem where the ListItem class in in my Common folder. I must not be binding these correctly to the view, but I do not know how to properly accomplish this?
Your ListPicker's templates are Static resources defined in Grid.Resources
Hence you have to change the ListPicker xaml code like the this
ItemTemplate="{Binding SearchProviderItemTemplate}"
FullModeItemTemplate="{Binding SearchProviderFullModeItemTemplate}"
replace the above two properties to the following
ItemTemplate="{StaticResource SearchProviderItemTemplate}"
FullModeItemTemplate="{StaticResource SearchProviderFullModeItemTemplate}"
If you are getting 'Project1.Common.ListItem' as the text that means your binding is correct but there is a problem with your template.
The first correction is as given above, you need to use "StaticResource" instead of "Binding" when you are referring to templates.
Please recheck your template for any typos etc.
If i were you, i would first try to make it work without using the Settings class, See if that helps
I develop app for Windows Phone 7 with using of Caliburn Micro.
Hear is a code of app's main parts.
Part of MainView:
<Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Panorama>
<controls:PanoramaItem x:Name="SubPanoramaItem"
DataContext="{Binding SubViewModel}">
<StackPanel>
<toolkit:ListPicker ExpansionMode="FullScreenOnly"
ItemsSource="{Binding DataModeList}">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Tag}" />
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel x:Name="item"
Margin="5, 24, 0, 24"
cal:Action.TargetWithoutContext="{Binding ElementName=SubPanoramaItem,
Path=DataContext}"
cal:Message.Attach="[Event Tap] = [Action Tap($dataContext)]"
Orientation="Horizontal">
<TextBlock FontSize="40"
Text="{Binding PopupText}" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
</StackPanel>
</controls:PanoramaItem>
<!-- Some other code -->
</controls:Panorama>
</Grid>
MainViewModel:
public class MainViewModel: Screen
{
public SubViewModel SubViewModel { get; private set; }
public MainViewModel(SubViewModel subViewModel)
{
SubViewModel = subViewModel;
}
// some other code
}
SubViewModel:
public class SearchViewModel : Screen
{
private ObservableCollection<DateModeItem> _dataModeList =
new ObservableCollection<DateModeItem>()
{
new DataItem
{ PopupText = "Item 1" },
new DataItem
{ PopupText = "Item 2" },
new DataItem
{ PopupText = "Item 3" },
new DataItem
{ PopupText = "Item 4" }
};
public ObservableCollection<DateModeItem> DataModeList
{
get
{
return _dataModeList;
}
private set { _dataModeList = value; }
}
public void Tap(object dataContext)
{
var item = dataContext as DataItem;
if (item != null)
{
var r = new Random();
switch (item.PopupText)
{
case "Item 1":
item.Tag = r.Next(5);
break;
case "Item 2":
item.Tag = r.Next(5, 10);
break;
case "Item 3":
item.Tag = r.Next(10, 15);
break;
case "Item 4":
item.Tag = r.Next(15, 20);
break;
}
}
}
}
DataItem:
public class DataItem
{
public string PopupText { get; set; }
public int Tag { get; set; }
}
As you can see I've attached Action to each StackPanel of DataTemplate in ListPicker. When tap occurs on the item in the list then new random tag must be generated. This tag is inserted into ListPicker's textbox.
And this actions behave very strangely. When I tap on 1, 2 and 4 item, nothing happens at all. When I tap 3 item the app throws exception - "No target found for method Tap". And this what happens when I'm using ListPicker from Silverlight Toolkit.
I've also triend RadListPicker from Telerik's RadConrols library. When I've used it, invocation of action method was unpredictable. Sometimes action invokes method correct. Sometimes nothing happen at all. One I can say surely - with tap on the last item it works less often in the correct way.
What is going on? I can't understand.
Additional info:
I've made a cleaning of all unnecessary stuff from my app and left only code that I described above in the previous post.
Now when I'm using ListPicker - nothing happens at all. List doesn't responds on taps. Sometimes app throws "No target found for method Tap" exception. When I'm using RadListPicker - almost always action not invoked and sometimes (very rarely) invoked correctly.
When you are working with ListPicker you need to add some ElementConventions in Order to bind the Actions of your View to your ViewModel.
Adding a binding convention can be done in the AppBootstrapper class. The code could look something like:
protected override void Configure()
{
ConventionManager.AddElementConvention<ListPicker>(ListPicker.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) => {
if (ConventionManager.GetElementConvention(typeof(ItemsControl)).ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager.ConfigureSelectedItem(element, ListPicker.SelectedItemProperty, viewModelType, path);
return true;
}
return false;
}; }