Reputation: 4661
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
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
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
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