Observable Collection not updating when adding item - xaml

I have tried everything but I cannot get to update my observable collection and be reflected in the UI. I have a method that adds a new entry to the collection that is bound to a CollectionViewSource which is bound to my collection. When I run the application Universal App I get the list correctly but if I add a value on click nothing gets reflected. Any suggestions?
The XAML looks like this:
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<ListView IsHoldingEnabled="True"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource MenuGroups}}"
ItemTemplate="{StaticResource MenuItemTemplate}"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource MenuGroupHeaderTemplate}"/>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<GridView Background="Black"
ItemsSource="{Binding Source={StaticResource MenuGroups}, Path=CollectionGroups}"
ItemTemplate="{StaticResource MenuJumpTemplate}">
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
Here is my code for the code behind:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using TestWin.TestWinService;
using System.Globalization;
using System.Collections.ObjectModel;
using System.ComponentModel;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace TestWin
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MenuPage : Page
{
#region Members
TestWinConnectionClient TestWinService;
ObservableCollection<Menu> ocMenuItems = null;
ObservableCollection<AlphaKeyGroup<Menu>> ocItemSource = null;
#endregion Members
#region Properties
public ObservableCollection<Menu> MenuItems
{
get
{
if (ocMenuItems == null)
{
ocMenuItems = new ObservableCollection<Menu>();
}
return ocMenuItems;
}
set
{
ocMenuItems = value;
OnPropertyChanged("MenuItems");
}
}
public ObservableCollection<AlphaKeyGroup<Menu>> ItemSource
{
get
{
if (ocItemSource == null)
{
ocItemSource = new ObservableCollection<AlphaKeyGroup<Menu>>((AlphaKeyGroup<Menu>.CreateGroups(MenuItems, CultureInfo.CurrentUICulture, s => s.MenuName, true)));
}
return ocItemSource;
}
set
{
ocItemSource = value;
OnPropertyChanged("ItemSource");
}
}
#endregion Properties
public MenuPage()
{
TestWinService = new TestWinConnectionClient();
this.InitializeComponent();
#region Events
this.Loaded += MenuPage_Loaded;
#endregion
}
private void MenuPage_Loaded(object sender, RoutedEventArgs e)
{
SetItemSource();
}
private async void SetItemSource()
{
MenuItems = await TestWinService.GetMenuEntriesAsync();
((CollectionViewSource)Resources["MenuGroups"]).Source = ItemSource;
}
private void TextBlock_Tapped(object sender, TappedRoutedEventArgs e)
{
Menu m = new Menu();
m.MenuName = "Test Entry";
m.SysRowID = Guid.NewGuid();
MenuItems.Add(m);
//this.Frame.Navigate(typeof(MenuPage));
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

If you add the new item to the MenuItems you also need to refresh the Source of CollectionViewSource.
Might be you have an ObservableCollection (ItemSource) that is not neccesary, you can take a look how I did it in Codeproject
and add for instance:
private async void Test()
{
await Task.Delay(5000);
for (int i = 0; i < 5; i++)
{
Favorites.Add(new Favorite()
{
Name = $"AAFriends {i}",
Category = new Category() { Name = "Friends" },
Uri = "http://www.expediteapps.com/blog/"
});
}
InitializeGrouping();
}
That refreshes only when I call the grouping again.

Related

Why is my binding to my picker not working with xaml/xamarin?

XAML Xamarin 5 - MacOS
XAML:
<Picker x:Name="StylesPicker" Title="Select stylesheet" HorizontalOptions="FillAndExpand" SelectedIndexChanged="StylesPicker_SelectedIndexChanged" SelectedItem="{Binding Path=vrrData.Style, Mode=TwoWay}"/>
Code behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppKit;
using VisitsRota.ViewModels;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace VisitsRota.Views
{
public partial class ElderlyInfirmPage : ContentPage
{
public ObservableCollection<string> StylesList { get; set; }
public ElderlyInfirmPage()
{
InitializeComponent();
BindingContext = new ElderlyInfirmViewModel();
string[] stylesArray = Directory.GetFiles("/Users/Shared/VisitsRota.MacOS/Styles", "ElderlyInfirm-Schedule-*.xsl")
.Select(file => Path.GetFileName(file)).ToArray<string>();
StylesList = new ObservableCollection<string>(stylesArray);
StylesPicker.ItemsSource = StylesList;
}
async private void InstallStyleButton_Clicked(object sender, EventArgs e)
{
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{DevicePlatform.macOS, new[] {"xsl"} }
});
var pickResult = await FilePicker.PickAsync(new PickOptions
{
FileTypes = customFileType,
PickerTitle = "Select template to install"
});
if(pickResult != null)
{
StylesList.Add(pickResult.FileName);
}
}
void StylesPicker_SelectedIndexChanged(System.Object sender, System.EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
if(selectedIndex != -1)
{
string x = (string)picker.ItemsSource[selectedIndex];
}
}
}
}
View Model:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
namespace VisitsRota.ViewModels
{
public class ElderlyInfirmViewModel : BaseViewModel
{
public List<string> StylesList { get; set; }
public List<string> ListMonths { get; }
public VisitsRotaData vrrData { get; set; }
public ElderlyInfirmViewModel()
{
vrrData = DeserializeFromXml<VisitsRotaData>("/Users/Shared/VisitsRota.MacOS/VisitsRotaData.xml");
// Should this list be private?
ListMonths = DateTimeFormatInfo.CurrentInfo.MonthNames.TakeWhile(m => m != String.Empty).ToList();
StylesList = Directory.GetFiles("/Users/Shared/VisitsRota.MacOS/Styles", "ElderlyInfirm-Schedule-*.xsl")
.Select(file => Path.GetFileName(file)).ToList<string>();
}
public T DeserializeFromXml<T>(string filePath)
{
try
{
if (!System.IO.File.Exists(filePath))
throw new ArgumentNullException(filePath + " not Exists");
using (System.IO.StreamReader reader = new System.IO.StreamReader(filePath))
{
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
T ret = (T)xs.Deserialize(reader);
return ret;
}
}
catch (Exception ex)
{
return default(T);
}
}
}
[XmlType(TypeName="VisitsRotaData")]
public class VisitsRotaData : BaseViewModel
{
private ObservableCollection<string> _listelders;
private ObservableCollection<string> _listpublishers;
private string _style;
[XmlArray("Elders")]
[XmlArrayItem("Elder")]
public ObservableCollection<string> ListElders
{
get { return _listelders; }
set {
_listelders = value;
OnPropertyChanged("ListElders");
}
}
[XmlArray("Publishers")]
[XmlArrayItem("Publisher")]
public ObservableCollection<string> ListPublishers
{
get { return _listpublishers; }
set
{
_listpublishers = value;
OnPropertyChanged("ListPublishers");
}
}
[XmlElement]
public string Style
{
get { return _style; }
set
{
_style = value;
OnPropertyChanged("Style");
}
}
}
}
XML Data:
<?xml version="1.0" encoding="UTF-8" ?>
<VisitsRotaData>
<Elders>
<Elder>Elder 1</Elder>
<Elder>Elder 2</Elder>
<Elder>Elder 3</Elder>
<Elder>Elder 4</Elder>
<Elder>Elder 5</Elder>
<Elder>Elder 6</Elder>
<Elder>Elder 7</Elder>
<Elder>Elder 8</Elder>
</Elders>
<Publishers>
<Publisher>Publisher 1</Publisher>
<Publisher>Publisher 2</Publisher>
<Publisher>Publisher 3</Publisher>
<Publisher>Publisher 4</Publisher>
<Publisher>Publisher 5</Publisher>
<Publisher>Publisher 6</Publisher>
<Publisher>Publisher 7</Publisher>
<Publisher>Publisher 8</Publisher>
<Publisher>Publisher 9</Publisher>
<Publisher>Publisher 10</Publisher>
</Publishers>
<Style>ElderlyInfirm-Schedule-v2.xsl</Style>
</VisitsRotaData>
The Picker populates with with the files from the folder. That is fine. But when the window displays it has the first item from the picker selected and not the second (which is the value of Styles.
Expected behaviour:
Bind the value of the Picker to the Styles property.
Picker defaults to the existing value.
It looks like I have worked it out. 😊 This answer here pointed me in the right direction.
XAML
I changed the Binding in the XAML like this for the Picker:
<Picker x:Name="StylesPicker" Title="Select stylesheet"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding StylesList}"
SelectedItem="{Binding Path=vrrData.Style, Mode=TwoWay}"/>
As you can see, I have also removed the StylesPicker_SelectedIndexChanged handler.
Code Behind
I removed the public ObservableCollection<string> StylesList { get; set; } from this class.
View Model
I pasted in the aforementioned StylesList property and populated it in the ElderlyInfirmViewModel constructor.
The Style property remained unchanged in the VisitsRotaData class.
Sample
Now, when my window displays, the Picker defaults to the value from my XML file:
I think I was complicating it by having the ItemSource in the code behind and the SelectedItem in the view model. By keeping it all in the one place (the BindingContext of the Page) it works.

How to show current date time in Textblock in TopAppBar-WinRT

i am trying to show the current sysdatetime in Top App bar and i was wondering anyway i can do that in XAML for win store apps.
public class MainViewModel : INotifyPropertyChanged
{
private readonly DispatcherTimer _timer = new DispatcherTimer();
private string _resDateTime;
public string ResDateTime
{
get
{
return _resDateTime;
}
set
{
_resDateTime = value;
NotifyPropertyChanged("ResDateTime");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
public MainViewModel()
{
_timer.Tick += TimerOnTick;
_timer.Start();
}
private void TimerOnTick(object sender, object o)
{
ResDateTime = DateTime.Now.ToString();
}
}
add to the code behind
public MainPage()
{
this.InitializeComponent();
DataContext = new MainViewModel();
}
and put on xaml
<Page.TopAppBar>
<AppBar>
<TextBlock Text="{Binding ResDateTime}"></TextBlock>
</AppBar>
</Page.TopAppBar>
hope it will help
In your page you can set Page.TopAppBar, and Page.BottomAppBar like this:
<Page.TopAppBar>
<AppBar>
<TextBlock Text="Your text" />
</AppBar>
</Page.TopAppBar>
From there, you can whether bind the Text property, if you're using the MVVM pattern, or simply assign a value in the code behind of the page, by giving a name to the TextBlock element.

How binding data depending on the value?

I have ObservableCollection and value that need to find the item in the collection. any ideas? (p.s converter not good idea, because i have many collections)
This functionality (applying a filter) belongs into the ViewModel. Here is an easy example for illustration.
You might also want to look at the CollectionViewSource for a more refined version of the same concept.
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<ListBox ItemsSource="{Binding MyClasses}" DisplayMemberPath="Name" Margin="5" />
<ListBox ItemsSource="{Binding MyFilteredClasses}" DisplayMemberPath="Name" Margin="5" />
<TextBox Text="{Binding MySelectedClass.Name}" Margin="5" />
</StackPanel>
</Window>
ViewModel:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
namespace WpfApplication1
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Class1> _myClasses;
public ObservableCollection<Class1> MyClasses { get { return _myClasses; } set { _myClasses = value; OnPropertyChanged("MyClasses"); } }
private List<Class1> _myFilteredClasses;
public List<Class1> MyFilteredClasses { get { return _myFilteredClasses; } set { _myFilteredClasses = value; OnPropertyChanged("MyFilteredClasses"); } }
private Class1 _mySelectedClass;
public Class1 MySelectedClass { get { return _mySelectedClass; } set { _mySelectedClass = value; OnPropertyChanged("MySelectedClass"); } }
public ViewModel()
{
MyClasses = new ObservableCollection<Class1>()
{
new Class1() { Name = "Connelly" },
new Class1() { Name = "Donnelly" },
new Class1() { Name = "Fonnelly" },
new Class1() { Name = "McGregor" },
new Class1() { Name = "Griffiths" }
};
// filter your ObservableCollection by some criteria, and bind to the result (either another list, or just one item)
MyFilteredClasses = MyClasses.Where(c => c.Name.EndsWith("onnelly")).ToList();
MySelectedClass = MyClasses.FirstOrDefault(c => c.Name.StartsWith("Mc"));
}
}
public class Class1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _name;
public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } }
}
}

Access named item inside ItemTemplate

I have the following scenario:
<Button Click="ClickHandler">Click Me</Button>
<TextBox x:Name="MyInputTextBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If MyRepeatTextBox.Text == MyInputTextBox.Text I want to change the color of MyRepeatTextBox.Background to Green. If MyRepeatTextBox.Text is blank I want to change the color to red. How would I implement the button click handler?
Not sure a button event would be the best for this.
As DataTriggers are again not brought outside the WPF world, those are out. There's no IMultiValueConverter either. Behaviours are currently out, but there is a nice codeplex project for them. I would use that
public class MatchTextForegroundBehaviour : Behavior<TextBox>
{
private TextBox _attachedElement;
public static readonly DependencyProperty MatchForegroundProperty =
DependencyProperty.Register("MatchForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Green), OnMatchForegroundChanged));
public static readonly DependencyProperty FallbackForegroundProperty =
DependencyProperty.Register("FallbackForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Black), OnFallbackForegroundChanged));
public static readonly DependencyProperty TextToMatchProperty =
DependencyProperty.Register("TextToMatch", typeof(string),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(null, OnTextToMatchChanged));
public Brush MatchForeground
{
get { return (Brush)GetValue(MatchForegroundProperty); }
set { SetValue(MatchForegroundProperty, value); }
}
public Brush FallbackForeground
{
get { return (Brush)GetValue(FallbackForegroundProperty); }
set { SetValue(FallbackForegroundProperty, value); }
}
public string TextToMatch
{
get { return (string)GetValue(TextToMatchProperty); }
set { SetValue(TextToMatchProperty, value); }
}
/// <summary>
/// Event when the behavior is attached to a element.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
_attachedElement = AssociatedObject;
if(_attachedElement != null)
_attachedElement.TextChanged += (s,e) => ChangeForeground();
}
private static void OnMatchForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnTextToMatchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnFallbackForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private void ChangeForeground()
{
if (_attachedElement == null) return;
if (string.IsNullOrEmpty(TextToMatch)) return; // change foreground to red?
if (_attachedElement.Text == TextToMatch)
{
_attachedElement.Foreground = MatchForeground;
}
else
{
_attachedElement.Foreground = FallbackForeground;
}
}
}
And the xaml
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}"
TextToMatch="{Binding Text, ElementName=MyInputTextBox}"
FallbackForeground="Black" MatchForeground="Green" />
If a button click event is really how you want to do this, you can try the following. I have not compiled this against WinRT, but I think everything used is in WinRT.
Use the following ExtensionMethod
internal static class TreeExtensions
{
public static T GetChildElement<T>(this DependencyObject element) where T :FrameworkElement
{
if (element == null) return null;
if(element.GetType() == typeof(T)) return (T)element;
T childAsT = null;
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(element, i);
childAsT = child.GetChildElement<T>();
if (childAsT != null) break;
}
return childAsT;
}
}
Inside the button click event you would do the following (assuming you gave the ItemsControl a name of itemsControl:
foreach (var item in itemsControl.Items)
{
var element = itemsControl.ItemContainerGenerator.ContainerFromItem(item);
var textblock = element.GetChildElement<TextBlock>();
if (textblock != null)
{
if (textblock.Text == MyInputTextBox.Text)
textblock.Foreground = new SolidColorBrush(Colors.Green);
else
textblock.Foreground = new SolidColorBrush(Colors.Black);
}
}

Bind enum to telerik column in silverlight

I've found the example at telerik forum of binding enum to radgridview, but it doesn't work hwo I need, and I need it on today, so i need a help.
Below are classes of this example, and only difference which I need is possibility to change value of positionof players. Now I can only change position by typing new value, but I need to select it from comboboxcolumn.
How can I do it?
<UserControl x:Class="BindingGridViewToEnumCollection.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:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:my="clr-namespace:BindingGridViewToEnumCollection"
mc:Ignorable="d" d:DesignHeight="700" d:DesignWidth="700">
<UserControl.Resources>
<my:MyViewModel x:Key="MyViewModel"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
Background="White"
DataContext="{StaticResource MyViewModel}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<telerik:RadGridView Name="playersGrid" Grid.Row="0"
ItemsSource="{Binding Players}"
AutoGenerateColumns="False">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Number}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Position}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Country}"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</Grid>
</UserControl>
My ViewModel
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
namespace BindingGridViewToEnumCollection
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Club> clubs;
private ObservableCollection<Player> players;
private object selectedItem;
//public IEnumerable<string> AssignedPositions
public IEnumerable<Position> AssignedPositions
{
get
{
return new[] { Position.DF, Position.FW };
}
}
public ObservableCollection<Club> Clubs
{
get
{
if (this.clubs == null)
{
this.clubs = Club.GetClubs();
}
return this.clubs;
}
}
public ObservableCollection<Player> Players
{
get
{
if (this.players == null)
{
this.players = Player.GetPlayers();
}
return this.players;
}
}
public object SelectedItem
{
get
{
return this.selectedItem;
}
set
{
if (value != this.selectedItem)
{
this.selectedItem = value;
this.OnPropertyChanged("SelectedItem");
}
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
private void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
}
Class Club:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
namespace BindingGridViewToEnumCollection
{
/// <summary>
/// A football club.
/// </summary>
public class Club : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
private DateTime established;
private int stadiumCapacity;
private ObservableCollection<Player> players;
public string Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
public DateTime Established
{
get { return this.established; }
set
{
if (value != this.established)
{
this.established = value;
this.OnPropertyChanged("Established");
}
}
}
public int StadiumCapacity
{
get { return this.stadiumCapacity; }
set
{
if (value != this.stadiumCapacity)
{
this.stadiumCapacity = value;
this.OnPropertyChanged("StadiumCapacity");
}
}
}
public ObservableCollection<Player> Players
{
get
{
if (null == this.players)
{
this.players = new ObservableCollection<Player>();
}
return this.players;
}
}
public Club()
{
}
public Club(string name, DateTime established, int stadiumCapacity)
{
this.name = name;
this.established = established;
this.stadiumCapacity = stadiumCapacity;
}
public Club(string name, DateTime established, int stadiumCapacity, ObservableCollection<Player> players)
: this(name, established, stadiumCapacity)
{
this.players = players;
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
private void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return this.Name;
}
public static ObservableCollection<Club> GetClubs()
{
ObservableCollection<Club> clubs = new ObservableCollection<Club>();
Club club;
// Liverpool
club = new Club("Liverpool", new DateTime(1892, 1, 1), 45362);
club.Players.Add(new Player("Pepe Reina", 25, Position.GK, "Spain"));
club.Players.Add(new Player("Jamie Carragher", 23, Position.DF, "England"));
club.Players.Add(new Player("Steven Gerrard", 8, Position.MF, "England"));
club.Players.Add(new Player("Fernando Torres", 9, Position.FW, "Spain"));
clubs.Add(club);
// Manchester Utd.
club = new Club("Manchester Utd.", new DateTime(1878, 1, 1), 76212);
club.Players.Add(new Player("Edwin van der Sar", 1, Position.GK, "Netherlands"));
club.Players.Add(new Player("Rio Ferdinand", 5, Position.DF, "England"));
club.Players.Add(new Player("Ryan Giggs", 11, Position.MF, "Wales"));
club.Players.Add(new Player("Wayne Rooney", 10, Position.FW, "England"));
clubs.Add(club);
// Chelsea
club = new Club("Chelsea", new DateTime(1905, 1, 1), 42055);
club.Players.Add(new Player("Petr ÄŒech", 1, Position.GK, "Czech Republic"));
club.Players.Add(new Player("John Terry", 26, Position.DF, "England"));
club.Players.Add(new Player("Frank Lampard", 8, Position.MF, "England"));
club.Players.Add(new Player("Nicolas Anelka", 39, Position.FW, "France"));
clubs.Add(club);
// Arsenal
club = new Club("Arsenal", new DateTime(1886, 1, 1), 60355);
club.Players.Add(new Player("Manuel Almunia", 1, Position.GK, "Spain"));
club.Players.Add(new Player("Gaël Clichy", 22, Position.DF, "France"));
club.Players.Add(new Player("Cesc Fàbregas", 4, Position.MF, "Spain"));
club.Players.Add(new Player("Robin van Persie", 11, Position.FW, "Netherlands"));
clubs.Add(club);
return clubs;
}
}
}
class Player
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Linq;
namespace BindingGridViewToEnumCollection
{
/// <summary>
/// A football player.
/// </summary>
public class Player : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
private int number;
private Position position;
private string country;
public string Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
}
public int Number
{
get { return this.number; }
set
{
if (value != this.number)
{
this.number = value;
this.OnPropertyChanged("Number");
}
}
}
public Position Position
{
get { return this.position; }
set
{
if (value != this.position)
{
this.position = value;
this.OnPropertyChanged("Position");
}
}
}
public string Country
{
get { return this.country; }
set
{
if (value != this.country)
{
this.country = value;
this.OnPropertyChanged("Country");
}
}
}
public Player()
{
}
public Player(string name, int number, Position position, string country)
{
this.name = name;
this.number = number;
this.position = position;
this.country = country;
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
private void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return this.Name;
}
public static ObservableCollection<Player> GetPlayers()
{
return new ObservableCollection<Player>(Club.GetClubs().SelectMany(c => c.Players));
}
}
}
namespace BindingGridViewToEnumCollection
{
/// <summary>
/// A football position.
/// </summary>
public enum Position
{
GK,
DF,
MF,
FW
}
}
EDIT
I'm using 2010.1.603.1040 version telerik, so I can't do i tlike there
http://demos.telerik.com/silverlight/#GridView/EnumDataSource
Your DataMemberBinding is wrong. You should add the mode:
DataMemberBinding="{Binding Position,Mode=TwoWay}"
<telerik:GridViewComboBoxColumn DataMemberBinding="{Binding Position,Mode=TwoWay}"
ItemsSource="{Binding Positions}">
<telerik:GridViewComboBoxColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Position}" />
</DataTemplate>
</telerik:GridViewComboBoxColumn.CellTemplate>
</telerik:GridViewComboBoxColumn>
One thing you can do is to add the following field and property to your MyViewModel class:
private static readonly Position[] positions = new Position[]
{
Position.GK, Position.DF, Position.MF, Position.FW
};
public IEnumerable<Position> Positions
{
get { return positions; }
}
(There's no Enum.GetValues() in Silverlight, so we have to make our own collection of the enum's values.)
Then you can change the Position column of your RadGridView to the following:
<telerik:GridViewComboBoxColumn DataMemberBinding="{Binding Position}"
ItemsSource="{Binding Positions}">
<telerik:GridViewComboBoxColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Position}" />
</DataTemplate>
</telerik:GridViewComboBoxColumn.CellTemplate>
</telerik:GridViewComboBoxColumn>
All we really should need to get the combobox column working is to change the type of the column to GridViewComboBoxColumn and add an ItemsSource that specifies the items to show in the combobox. However, I found that the Position column had a habit of going blank when other columns were focused. Once I added the CellTemplate. I found that the Position column behaved itself a bit better.
Note that you will need to set the Mode of your bindings to TwoWay if you want the changes you make in the grid to be sent back to your view-model.