tags:

views:

91

answers:

2

I have a ViewModel

class FontsViewModel : ObservableObject
{
    public FontsViewModel()
    {
        InitFonts();
    }

    public ObservableCollection<Typeface> Fonts
    {
        get;
        private set;
    }

    protected void InitFonts()
    {
        Fonts = (ObservableCollection<Typeface>)System.Windows.Media.Fonts.SystemFontFamilies;
    }
}

and i bind it in XAML

<ribbon:RibbonComboBox.DataContext>
    <vm:FontsViewModel />
</ribbon:RibbonComboBox.DataContext>
<ribbon:RibbonGallery>
    <ribbon:RibbonGalleryCategory ItemsSource="{Binding Fonts}">
        <ribbon:RibbonGalleryCategory.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" FontFamily="{Binding}" />
            </DataTemplate>
        </ribbon:RibbonGalleryCategory.ItemTemplate>
    </ribbon:RibbonGalleryCategory>
</ribbon:RibbonGallery>

when i try running, i get

System.Windows.Markup.XamlParseException occurred
  Message='The invocation of the constructor on type 'MarkdownEditMVVM.ViewModels.FontsViewModel' that matches the specified binding constraints threw an exception.' Line number '57' and line position '26'.
  Source=PresentationFramework
  LineNumber=57
  LinePosition=26
  StackTrace:
       at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
       at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
       at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
       at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
       at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
       at MarkdownEditMVVM.MainWindow.InitializeComponent() in d:\Projects\MarkdownEdit\MarkdownEditMVVM\MainWindow.xaml:line 1
       at MarkdownEditMVVM.MainWindow..ctor() in D:\Projects\MarkdownEdit\MarkdownEditMVVM\MainWindow.xaml.cs:line 25
  InnerException: System.InvalidCastException
       Message=Unable to cast object of type 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.Windows.Media.FontFamily]' to type 'System.Collections.ObjectModel.ObservableCollection`1[System.Windows.Media.Typeface]'.
       Source=MarkdownEditMVVM
       StackTrace:
            at MarkdownEditMVVM.ViewModels.FontsViewModel.InitFonts() in D:\Projects\MarkdownEdit\MarkdownEditMVVM\ViewModels\FontsViewModel.cs:line 26
            at MarkdownEditMVVM.ViewModels.FontsViewModel..ctor() in D:\Projects\MarkdownEdit\MarkdownEditMVVM\ViewModels\FontsViewModel.cs:line 15
       InnerException: 

what am i doing wrong? its like a modified version of what i see on Lesters Tutorial on WPF Ribbon

+4  A: 

You cannot convert a SystemFontFamilies to an ObservableCollection. Instead instantiate a new collection:

  Fonts = new ObservableCollection<Typeface> (Fonts.SystemFontFamilies);

edit: The above code has a mismatch between the property type and the collection type.
Instead use one of the below:

        var myFonts = new ObservableCollection<FontFamily>(Fonts.SystemFontFamilies);

        var typefaces = new ObservableCollection<Typeface>(Fonts.SystemTypefaces);
Nescio
Hmm when i try this, I have renamed `Fonts` to `AvailableFonts` so that it doesn't collide with `System.Windows.Media.Fonts` but i still get errors ... [http://pastebin.com/eZ2PBfFJ](http://pastebin.com/eZ2PBfFJ). Oh and u say i cannot convert `SystemFontFamilies` to `ObservableCollection`, but i did do a cast?
jiewmeng
adding to this, `ObservableCollection` can be casted to `IEnumerable` but `IEnumerable` cannot (always) be casted to `ObservableCollection`. I say *always because not all `IEnumerable`s are `ObservableCollection`s but all `ObservableCollection`s are `IEnumberable`s. hope this clears some of your questions.
nathan_hc
@Nescio, thanks, i didn't see that :) @nathan_hc good point
jiewmeng
+3  A: 

Two points:

  1. You really don't need an ObservableCollection here (if the list of Fonts will never change.) It's sufficient to expose an IEnumerable<Typeface> property.

  2. You really shouldn't be referencing a View-specific object like a Typeface from a ViewModel. It would be better to expose the available font names as strings and inject the available names through some service interface. The service interface (on the View side) would then call System.Windows.Media.Fonts.SystemFontFamilies and cast to string names. This gives you isolation for testability (your unit tests can supply their own font names.)

Dan Bryant
I don't know if i did it the correct way, but i tried using LINQ to order and select the string (via `toString()`) but i still get error see [pastebin](http://pastebin.com/2Q3nZaXg)
jiewmeng
oh i found out the prev error was because Typeface dont implement IComparable so it can't order the selection. i changed it to `orderby font.toString`
jiewmeng
oh and regarding (2), for colors, i should use ARGB hex value?
jiewmeng
@jiewmeng, possibly; it's unusual for a view model to deal directly with colors and, for this particular case, you might bend the rules. That's because colors are really a kind of data in the model here. For the best isolation, you'd probably want to use a View-independent string (such as "#FFFF0000" or "Red"), but I could see an argument for using Color types throughout the Model and ViewModel for clarity.
Dan Bryant