Robby Smet
Robby Smet

Reputation: 4661

Can't convert object of type MS.Internal.NamedObject to System.Windows.Datatemplate

I want to change an icon based on an enum.

I've created a new viewmodel for my UserControl named CallControlViewModel

public class CallControlViewModel : BaseViewModel
{
    private InputTypeEnum _inputTypeEnum;

    public CallControlViewModel()
    {

    }

    public InputTypeEnum InputType
    {
        get { return _inputTypeEnum; }
        set
        {
            if (_inputTypeEnum != value)
            {
                _inputTypeEnum = value;
                NotifyPropertyChanged("InputType");
            }
        }
    }
}

This is the baseViewModel

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notify of Property Changed event
    /// </summary>
    /// <param name="propertyName"></param>
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }        
}

This is the enum

public enum InputTypeEnum
{
    Empty = 0, Number = 1, Text = 2
}

Code behind usercontrol

public partial class CallControl : UserControl
{
    private CallControlViewModel callControlViewModel;

    public CallControl()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(CallControl_Loaded);
    }

    void CallControl_Loaded(object sender, RoutedEventArgs e)
    {
        callControlViewModel = new CallControlViewModel();
        this.DataContext = callControlViewModel;
    }

    private void CallBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        InputTypeEnum type = DecideInputType();
        callControlViewModel.InputType = type;
    }

    private InputTypeEnum DecideInputType()
    {
        if(string.IsNullOrEmpty(CallBox.Text))
        {
            return InputTypeEnum.Empty;
        }

        if (IsNumeric(CallBox.Text))
        {
            return InputTypeEnum.Number;
        }

        return InputTypeEnum.Text;
    }

And this is my Xaml:

<UserControl.Resources>
    <Style x:Key="InputTypeIndicatorStyle" TargetType="{x:Type ContentControl}">
    <Style.Triggers>
           <DataTrigger Binding="{Binding InputType}" Value="0">
               <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=NumberIndicator}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding InputType}" Value="1">
                <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=NumberIndicator}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding InputType}" Value="2">
                <Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=TextIndicator}" />
             </DataTrigger>
            </Style.Triggers>
        </Style>

        <DataTemplate x:Key="NumberIndicator">
            <Border x:Name="CallIconBorder" Width="35" BorderThickness="1,0,0,0" Background="#353535" 
                    BorderBrush="#5d5d5d" MouseLeftButtonDown="CallIconBorder_MouseLeftButtonDown" Style="{StaticResource CallBorderStyle}" >
                <Image StretchDirection="DownOnly" Margin="5" Source="/Image/call.png"/>
            </Border>
        </DataTemplate>

        <DataTemplate x:Key="TextIndicator">
            <Border x:Name="SearchIconBorder" Width="35" >
                <Image StretchDirection="DownOnly" Margin="5" Source="/Image/search.png"/>
            </Border>
        </DataTemplate>
  </UserControl.Resources>

<DockPanel x:Name="CallControlDock" VerticalAlignment="Bottom" Background="{StaticResource LightGrey}" Height="30">
            <ContentControl Style="{StaticResource InputTypeIndicatorStyle}" DockPanel.Dock="Right" HorizontalAlignment="Right"  />

            <Border x:Name="ClearIconBorder" DockPanel.Dock="Right" Width="20" Visibility="Hidden" VerticalAlignment="Center" Margin="5,0,5,0" 
                    MouseDown="ClearIconBorder_MouseDown" Style="{StaticResource ClearIconStyle}" Opacity="0.5">
                <Image StretchDirection="DownOnly" Source="/Image/close.png" HorizontalAlignment="Left"/>
            </Border>

            <spinners:ucSpinnerCogs x:Name="LoadSpinner" DockPanel.Dock="Right" HorizontalAlignment="Right" Visibility="Collapsed" />

            <TextBox x:Name="CallBox" TextWrapping="Wrap" FontSize="14" FontFamily="Segoe UI Semibold" HorizontalAlignment="Stretch" 
                     Foreground="{StaticResource AlmostWhite}" VerticalAlignment="Center"  
                     GotFocus="CallBox_GotFocus" LostFocus="CallBox_LostFocus" TextChanged="CallBox_TextChanged" KeyDown="CallBox_KeyDown"
                     MouseRightButtonDown="CallBox_MouseRightButtonDown"
                     ContextMenu="{x:Null}">


            </TextBox>
        </DockPanel>

When I change the InputType property I get an error in the baseViewModel:

PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); ==>

InvalidCastException, Can't convert object of type MS.Internal.NamedObject to System.Windows.Datatemplate

What am I doing wrong?

Upvotes: 3

Views: 9406

Answers (3)

N. Kudryavtsev
N. Kudryavtsev

Reputation: 4071

For everyone who ran into the same issue but doesn't find another answers helpful in finding what exactly is going on.

The problem occurs when you try referencing resources with StaticResource before their declaration.

In this question these resources are NumberIndicator and TextIndicator.

It is happening because StaticResource works at compile-time and cannot look forward. So to solve the issue you can move the resouces to where they are not referenced yet. Or just use run-time DynamicResource.

Upvotes: 14

Sheridan
Sheridan

Reputation: 69985

Right, this whole post is one messy red herring... for anyone not familiar with that saying, it means that you can forget all about the error given because when this is done properly, you won't get that error... it's misleading.

So here we go... using your Trigger method:

First, here's an enum:

public enum TestEnum
{
    None, One, Two, Three
}

Now the properties:

private TestEnum enumInstance = TestEnum.None;

public TestEnum EnumInstance
{
    get { return enumInstance; }
    set { enumInstance = value; NotifyPropertyChanged("EnumInstance"); }
}

private ObservableCollection<TestEnum> enumCollection = 
    new ObservableCollection<TestEnum>() { TestEnum.None, TestEnum.One, 
    TestEnum.Two, TestEnum.Three };

public ObservableCollection<TestEnum> EnumCollection
{
    get { return enumCollection; }
    set { enumCollection = value; NotifyPropertyChanged("EnumCollection"); }
}

Now the XAML:

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
    <Image Width="16" Height="16" Stretch="None" Margin="0,0,0,20">
        <Image.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding EnumInstance}" Value="One">
                        <Setter Property="Image.Source" Value="/WpfApplication2;component/Images/Copy_16.png" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding EnumInstance}" Value="Two">
                        <Setter Property="Image.Source" Value="/WpfApplication2;component/Images/Edit_16.png" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding EnumInstance}" Value="Three">
                        <Setter Property="Image.Source" Value="/WpfApplication2;component/Images/CloseRed_16.png" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Image.Style>
    </Image>
    <ComboBox ItemsSource="{Binding EnumCollection}" SelectedItem="{Binding EnumInstance}" />
</StackPanel>

I trust that you can transfer this code to your project ok. One last thing to note... if you have any more enum values than this, you'd be better off creating an EnumToBoolImageSourceConverter and Binding with that instead.

Upvotes: 6

Paul Sinnema
Paul Sinnema

Reputation: 2792

I do remember this problem from a project some years ago. We had the same problem and added code to intercept that like so:

    /// <summary>
    /// Tests whether the object is the 'NamedObject'. This is placed into 'DataContext' sometimes by WPF as a dummy.
    /// </summary>
    public static bool IsNamedObject(this object obj)
    {
        return obj.GetType().FullName == "MS.Internal.NamedObject";
    }

We posted several questions about this on forums but never really got an answer

Upvotes: 2

Related Questions