I'm in progress to create a desktop application in C# with WPF but i'm stuck with my menu.
I have a left panel menu to switch between functionality.
I created one viewmodel with 2 views which need to be display in my main window. if i'm clicking on Monitoring, the content view switch from Home to Monitoring but when i'm on Monitoring, If I try to back on Home, it's not working and I don't understand why.
Below my project folder
([abc] = Folder; - abc = file) :
[PROJECT]
[assets]
- Home.png
- Stats.png
[ViewModels]
- MainWindowViewModel.cs
[Views]
- Home.xaml
- Home.cs
- Stats.xaml
- Stats.cs
- MainWindow.xaml
- MainWindow.cs
- App.xaml
- App.cs
- App.config
Below important part of my code :
MainWindow.xaml
<Window.Resources>
<DataTemplate x:Key="HomeViewTemplate" DataType="{x:Type viewmodels:MainWindowViewModel}">
<views:Home DataContext="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="StatsViewTemplate" DataType="{x:Type viewmodels:MainWindowViewModel}">
<views:Stats DataContext="{Binding}" />
</DataTemplate>
</Window.Resources>
<Grid Grid.Row="0" MouseLeftButtonDown="OpenHome">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image Source="/assets/home.png" Height="32" Width="32" />
<Label Content="Home" Grid.Column="1" Foreground="White" FontSize="12" VerticalAlignment="Center" Padding="0,5,5,5" />
</Grid>
<Grid Grid.Row="1" MouseLeftButtonDown="OpenStats">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image Source="/assets/stats.png" Height="32" Width="32" />
<Label Content="Monitoring" Grid.Column="1" Foreground="White" FontSize="12" VerticalAlignment="Center" Padding="0,5,5,5" />
</Grid>
<Grid Grid.Row="1">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource HomeViewTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Route}" Value="Home">
<Setter Property="ContentTemplate" Value="{StaticResource HomeViewTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Route}" Value="Stats">
<Setter Property="ContentTemplate" Value="{StaticResource StatsViewTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
MainWindow.cs
private MainWindowViewModel MV = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
}
private void OpenHome(object sender, RoutedEventArgs e)
{
MV.Route = "Home";
DataContext = MV;
}
private void OpenStats(object sender, RoutedEventArgs e)
{
MV.Route = "Stats";
DataContext = MV;
}
ViewModels>MainWindowViewModel.cs
public class MainWindowViewModel
{
public string Route
{
get;
set;
}
public MainWindowViewModel(){}
}
Send notifications about changes for Route property via INotifyPropertyChanged interface implementation:
public class MainWindowViewModel : INotifyPropertyChanged
{
public string _Route;
public string Route
{
get { return _Route; }
set { _Route = value; OnPropertyChanged("Route"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowViewModel(){}
}
Such notification will activate DataTrigger which in turn will change ContentTemplate
Related
I have a Grid, and in that grid, I have this:
<StackPanel Grid.Row="2"
Grid.Column="0">
<Grid x:Name="GridButtonItem" Margin="30,0,0,5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="#332a8dd4" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="False">
<Setter Property="Background"
Value="Transparent" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Margin="3"
Source="{dx:DXImageOffice2013 Image=Windows_32x32.png}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<TextBlock Grid.Row="0"
Grid.Column="1"
Margin="10,3,3,0"
Text="Application Log" />
<TextBlock Grid.Row="1"
Grid.Column="1"
Margin="10,0,3,3"
Text="C:\Program Files (x86)\ATI Technologies\ATI.ACE\MOM-InstallProxy" />
</Grid>
</StackPanel>
The StackPanel is actually meant to hold many of the GridButtonItem items. Is there a way that I can somehow make a "template" of GridButtonItem and then for each one I want to add to the StackPanel, just set the Image and Text properties?
Something like this (just pseudo-code for demonstration):
<StackPanel>
<Grid Template="myGridItemTemplate">
<Setter Property="Image" Value="img1.png"/>
<Setter Property="Text1" Value="button1 Text"/>
<Setter Property="Text2" Value="button2 Text"/>
</Grid>
<Grid Template="myGridItemTemplate">
<Setter Property="Image" Value="img1.png"/>
<Setter Property="Text1" Value="button1 Text"/>
<Setter Property="Text2" Value="button2 Text"/>
</Grid>
<Grid Template="myGridItemTemplate">
<Setter Property="Image" Value="img1.png"/>
<Setter Property="Text1" Value="button1 Text"/>
<Setter Property="Text2" Value="button2 Text"/>
</Grid>
</StackPanel>
So each one that is added picks up the row/column definitions, and an embedded Image and two TextBlocks. Then I just set the three properties for each one added.
Is this possible?
You can put your grid control into a UserControl and then reuse the UserControl throughout your project. I have a simple example of doing this with a label and Textbox.
here is the XAML:
<UserControl x:Class="TestVision.CustomControls.LabelAndTextbox"
x:Name="parent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestVision.CustomControls"
mc:Ignorable="d" >
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=parent}">
<TextBlock Text="{Binding Path=Label}" Width="{Binding Path=LabelWidth}" VerticalAlignment="Center" TextAlignment="Right" Margin="0,0,10,0" Height="22"/>
<TextBox Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" Width="{Binding Path=TextboxWidth}" IsReadOnly="{Binding Path=TextboxReadOnly, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="{Binding Path=TextboxHorizontalContentAlgnment}"/>
</StackPanel>
</UserControl>
Any properties that you want to be able to set e.g. your image text etc. must be bound to Dependency Properties in the code behind.
Code behind:
public partial class LabelAndTextbox : UserControl
{
/// <summary>
/// Gets or sets the Label which is displayed next to the field
/// </summary>
public String Label
{
get { return (String)GetValue(LabelContent); }
set { SetValue(LabelContent, value); }
}
/// <summary>
/// Identified the Label dependency property
/// </summary>
public static readonly DependencyProperty LabelContent =
DependencyProperty.Register("Label", typeof(string),
typeof(LabelAndTextbox), new PropertyMetadata(""));
public object Text
{
get { return (object)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(object),
typeof(LabelAndTextbox), new PropertyMetadata(null));
public Double LabelWidth
{
get { return (Double)GetValue(LabelWidthProperty); }
set { SetValue(LabelWidthProperty, value); }
}
public static readonly DependencyProperty LabelWidthProperty =
DependencyProperty.Register("LabelWidth", typeof(Double),
typeof(LabelAndTextbox), new PropertyMetadata());
public Double TextboxWidth
{
get { return (Double)GetValue(TextboxWidthProperty); }
set { SetValue(TextboxWidthProperty, value); }
}
public static readonly DependencyProperty TextboxWidthProperty =
DependencyProperty.Register("TextboxWidth", typeof(Double),
typeof(LabelAndTextbox), new PropertyMetadata());
public bool TextboxReadOnly
{
get { return (bool)GetValue(TextboxReadOnlyProperty); }
set { SetValue(TextboxReadOnlyProperty, value); }
}
public static readonly DependencyProperty TextboxReadOnlyProperty =
DependencyProperty.Register("TextboxReadOnly", typeof(bool),
typeof(LabelAndTextbox), new FrameworkPropertyMetadata());
public HorizontalAlignment TextboxHorizontalContentAlgnment
{
get { return (HorizontalAlignment)GetValue(TextboxHorizontalContentAlgnmentProperty); }
set { SetValue(TextboxHorizontalContentAlgnmentProperty, value); }
}
public static readonly DependencyProperty TextboxHorizontalContentAlgnmentProperty =
DependencyProperty.Register("TextboxHorizontalContentAlgnment", typeof(HorizontalAlignment),
typeof(LabelAndTextbox), new FrameworkPropertyMetadata());
public LabelAndTextbox()
{
InitializeComponent();
}
}
you then will need to add a reference in the XAML file to your UserControl like this:
xmlns:Resource="clr-namespace:ProjectNamespace.FolderContainingYourControl"
Resource is a generic identifier you can call it what you like, you can then reference your control in the like this:
<Resource:LabelAndTextblock x:Name="AddressLine1" Label="{Binding LblTxt_AddressLine1}" Text="{Binding AddressLine1, Mode=TwoWay}" Margin="10,5,0,5" LabelWidth="70" TextWidth="250" TextHeight="60"/>
You could do this with a UserControl (two different ways) or a DataTemplate. Let's go with DataTemplate, because stuicidle already ably demonstrated one UserControl approach.
There are a couple of different ways to do this with a DataTemplate, too.
We're going to do something called an implicit DataTemplate. It's created in Resources, but it has no x:Key property, just a DataType="{x:Type local:GridItemViewModel}" property. What that will do is this: Wherever that DataTemplate is in scope, whenever XAML needs to display a GridItemViewModel and nothing is specifying a template to display it in, it'll use that implicit template.
Clear as mud! Welcome to the XAML learning curve.
ViewModels.cs
using System;
using System.ComponentModel;
using System.Windows.Media;
namespace GridItemAnswer
{
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
#region GridItemViewModel Class
public class GridItemViewModel : ViewModelBase
{
#region LabelText Property
private String _labelText = null;
public String LabelText
{
get { return _labelText; }
set
{
if (value != _labelText)
{
_labelText = value;
OnPropertyChanged();
}
}
}
#endregion LabelText Property
#region Path Property
private String _path = null;
public String Path
{
get { return _path; }
set
{
if (value != _path)
{
_path = value;
OnPropertyChanged();
}
}
}
#endregion Path Property
#region ImageSource Property
private ImageSource _imageSource = null;
public ImageSource ImageSource
{
get { return _imageSource; }
set
{
if (value != _imageSource)
{
_imageSource = value;
OnPropertyChanged();
}
}
}
#endregion ImageSource Property
}
#endregion GridItemViewModel Class
}
MainWindow.xaml
<Window
x:Class="GridItemAnswer.MainWindow"
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:local="clr-namespace:GridItemAnswer"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<DataTemplate DataType="{x:Type local:GridItemViewModel}">
<StackPanel>
<Grid x:Name="GridButtonItem" Margin="30,0,0,5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#332a8dd4" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Margin="3"
Source="{Binding Image}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="10,3,3,0"
Text="{Binding LabelText}"
/>
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="10,0,3,3"
Text="{Binding Path}"
/>
</Grid>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ItemsControl>
<local:GridItemViewModel
LabelText="Foo Bar"
Path="c:\foo\bar"
/>
<local:GridItemViewModel
LabelText="Baz Planxty"
Path="c:\baz\planxty"
/>
</ItemsControl>
<Label>
<local:GridItemViewModel
LabelText="A frog walks into a bank asking for a loan"
Path="c:\knick\knack"
/>
</Label>
</StackPanel>
</Grid>
</Window>
I am building a WPF application using MVVM Light. In it I have a dialog box. The XAML:
<Window x:Class="ParserEditor.NewParserDialog"
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:local="clr-namespace:ParserEditor"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
DataContext="{Binding NewParser, Source={StaticResource Locator}}"
Title="New Parser..."
SizeToContent="Height"
Width="300">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
<BitmapImage x:Key="ErrorImage" UriSource="Resources/Error.png" />
<local:BooleanToVisibilityConverter x:Key="BoolToVisiblity" True="Visible" False="Collapsed" />
<ControlTemplate x:Key="InputErrorTemplate">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="16"
Margin="5"
Source="{StaticResource ErrorImage}"
ToolTip="Contains invalid data"
VerticalAlignment="Center"
Width="16" />
<Border BorderBrush="Red"
BorderThickness="2">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="ComboBox">
<Setter Property="Margin" Value="5,4,26,4" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Name="PromptLabel"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="0"
Text="{Binding Path=Prompt, Mode=TwoWay}"
Visibility="{Binding Path=HasPrompt, Converter={StaticResource BoolToVisiblity}}"/>
<TextBlock Name="ParserTypeLabel"
Grid.Column="0"
Grid.Row="2"
Text="Parser Type:" />
<ComboBox Name="ParserTypePicker"
Grid.Column="1"
Grid.Row="2"
ItemsSource="{Binding Path=ParserTypes}"
SelectedItem="{Binding Path=ParserType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<Grid Name="ButtonGrid"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Name="OkButton"
Command="{Binding CloseCommand, ValidatesOnDataErrors=True}"
Grid.Column="0"
Content="OK"
IsDefault="True" />
<Button Name="CancelButton"
Grid.Column="1"
Content="Cancel"
IsCancel="True" />
</Grid>
</Grid>
</Window>
The View Model object:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using ParserEditor.Model;
namespace ParserEditor.ViewModel {
public class NewParserViewModel : ViewModelBase, IDataErrorInfo {
private readonly IDataService _dataService;
public string ParserType {
get { return _ParserType; }
set { Set( ref _ParserType, value ); }
}
private string _ParserType;
public ObservableCollection<string> ParserTypes { get; private set; }
public bool HasPrompt {
get { return !string.IsNullOrWhiteSpace( Prompt ); }
}
public string Prompt {
get { return _Prompt; }
set {
Set( ref _Prompt, value );
RaisePropertyChanged( nameof( HasPrompt ) );
}
}
private string _Prompt;
#region CloseCommand
public RelayCommand CloseCommand { get; private set; }
private bool CanCloseDialog() {
return ParserType == DataService.AWK_FORMAT ||
ParserType == DataService.CSHARP_FORMAT ||
ParserType == DataService.REGEX_FORMAT;
}
private void CloseDialog() {
Messenger.Default.Send( new CloseWindowMessage() );
}
#endregion
#region IDataErrorInfo Implementation
public string Error {
get { return this[ "ParserType" ]; }
}
public string this[ string columnName ] {
get {
switch ( columnName ) {
case "ParserType":
return string.IsNullOrWhiteSpace( ParserType ) ? "You must choose a Parser Type" : null;
default:
return null;
}
}
}
#endregion
public NewParserViewModel( IDataService dataService ) {
_dataService = dataService;
CloseCommand = new RelayCommand( CloseDialog, CanCloseDialog );
ParserTypes = new ObservableCollection<string>();
ParserTypes.Add( DataService.AWK_FORMAT );
ParserTypes.Add( DataService.CSHARP_FORMAT );
ParserTypes.Add( DataService.REGEX_FORMAT );
}
}
}
I've placed a breakpoint in the CanCloseDialog method and it only gets hit once, when the dialog is first displayed. If I select a choice in the ComboBox after the dialog is displayed, the OK button doesn't enable.
What am I missing?
I did some more searching & I finally found the answer here. It turns out I had to change the
using GalaSoft.MvvmLight.Command;
statement to
GalaSoft.MvvmLight.CommandWpf;
Doing this, everything works properly.
Try a delegate Func wich call CanCloseDialog instead.
Somehow RelayCommand lose pointer to the method whithout a delegate.
Invoke the delegate in the CanExecute method of the RelayCommand implementation.
Something like :
CloseCommand = new RelayCommand( CloseDialog,() => {return <your condition logic>});
So I have a custom class that is designed to contain menu items and sub menu items formatted as such:
public class ApplicationMenuItem
{
public ImageSource Image { get; set; }
public string Text { get; set; }
public string Tooltip { get; set; }
public ICollection<ApplicationMenuItem> Items { get; set; }
public EventHandler Clicked {get;set;}
public void Click(object sender, EventArgs e)
{
if (Clicked!=null)
Clicked(this, e);
}
public ApplicationMenuItem(string Text)
{
this.Text = Text;
Items = new List<ApplicationMenuItem>();
}
public ApplicationMenuItem()
{
Items = new List<ApplicationMenuItem>();
}
}
Before anybody asks why I don't inherit Menu or just create a Menu object and bind it, its because this class may be used on platforms and frameworks that don't necessarily use the Menu UI object, not to mention this class will drive nav menus, context menus, sidebars, toolbars etc....
My question is as you can see I have a self referencing list Items contained within to allow sub menus; binding the first level menu elements is easy enough, but how do I recursively bind sub elements while creating a template for its elements in WPF?
Here's an example of a recursive XAML template using your ApplicationMenuItem class exactly as you defined it (except that I put it in a namespace called Wobbles). This is not finished, releasable code. But it demonstrates a recursive DataTemplate, and some bonus goodies like displaying the popup. You can add an IsEnabled property to your menu item class and implement it in the XAML with additional trigger that sets colors, and an additional condition in the multitrigger that drives SubmenuPopup.IsOpen. If you want to support horizontal separators, you could add a property bool ApplicationMenuItem.IsSeparator and give the template a trigger which replaces the grid content below with a horizontal line when that property is True.
RecursiveTemplate.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbl="clr-namespace:Wobbles"
>
<DataTemplate DataType="{x:Type wbl:ApplicationMenuItem}">
<Grid
Name="RootGrid"
Background="BlanchedAlmond"
Height="Auto"
UseLayoutRounding="True"
SnapsToDevicePixels="True"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="24" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="{Binding Image}"
/>
<Label
Grid.Column="1"
Content="{Binding Text}"
/>
<Border
Name="PopupGlyphBorder"
Grid.Column="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{Binding ElementName=RootGrid, Path=Background}"
>
<Path
Height="10"
Width="5"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Data="M 0,0 L 5,5 L 0,10 Z"
Fill="Black"
/>
</Border>
<Popup
Name="SubmenuPopup"
PlacementTarget="{Binding ElementName=PopupGlyphBorder}"
Placement="Right"
StaysOpen="True"
>
<Border
BorderBrush="DarkGoldenrod"
BorderThickness="1"
>
<ItemsControl
Name="SubmenuItems"
ItemsSource="{Binding Items}"
/>
</Border>
</Popup>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="RootGrid" Property="Background" Value="Wheat" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition SourceName="SubmenuItems" Property="HasItems" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="SubmenuPopup" Property="IsOpen" Value="True" />
</MultiTrigger>
<Trigger SourceName="SubmenuItems" Property="HasItems" Value="False">
<Setter TargetName="PopupGlyphBorder" Property="Visibility" Value="Hidden" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
MainWindow.xaml
<Window
x:Class="RecursiveTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wbl="clr-namespace:Wobbles"
Title="MainWindow"
Height="350"
Width="525"
>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="RecursiveTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<wbl:TestViewModel />
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<ContentControl
Content="{Binding Menu}"
Width="100"
Height="24"
/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
ViewModel.cs
namespace Wobbles
{
public class TestViewModel
{
public TestViewModel()
{
Menu = CreateMenu();
}
public Wobbles.ApplicationMenuItem Menu { get; protected set; }
protected Wobbles.ApplicationMenuItem CreateMenu()
{
var m = new Wobbles.ApplicationMenuItem("Menu");
var msub = new Wobbles.ApplicationMenuItem("Submenu");
msub.Items.Add(new Wobbles.ApplicationMenuItem("Sub Sub 1"));
msub.Items.Add(new Wobbles.ApplicationMenuItem("Sub Sub 2"));
// LOL
msub.Items.Add(msub);
m.Items.Add(msub);
m.Items.Add(new Wobbles.ApplicationMenuItem("Foo"));
m.Items.Add(new Wobbles.ApplicationMenuItem("Bar"));
m.Items.Add(new Wobbles.ApplicationMenuItem("Baz"));
return m;
}
}
}
Nits, Cavils, Kvetches, and a Brief Homily
Working with XAML, I'd suggest making a practice of using ObservableCollection<T> instead of List<T>. If the items in the collection change after the UI is constructed, ObservableCollection<T> will cause the UI to update appropriately. For the same reason, you'll want ApplicationMenuItem to implement INotifyPropertyChanged. I'd also prefer supporting an ICommand Command property as well as the Click event, and I'd further name the Click event Click in accordance with standard XAML practice.
"What Would XAML Do?" You'll pretty much never go wrong if you do your utmost to write code that could be mistaken for the standard library that shipped with the environment you're working in.
I have a TabViewModel wich contains a dependency property CurrentViewModel. The CurrentViewModel property is bound to a ContentControl in the view TabView.xaml. The TabViewModel also contains a command to change the CurrentViewModel to a ProductViewModel:
public class TabViewModel: BaseViewModel
{
public string TabName { get; set; }
//public List<BaseViewModel> ViewModels { get; set; }
private PageViewModel _currentViewModel;
public PageViewModel CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
OnPropertyChanged("CurrentViewModel");
}
}
public TabViewModel(string tabName, PageViewModel currentViewModel)
{
TabName = tabName;
CurrentViewModel = currentViewModel;
}
private ICommand _navigateToProductViewModelCommand;
public ICommand NavigateToProductViewModelCommand
{
get
{
if (_navigateToProductViewModelCommand == null)
{
_navigateToProductViewModelCommand = new DelegateCommand<Product>(
(p) =>
{
CurrentViewModel = new ProductViewModel();
});
}
return _navigateToProductViewModelCommand;
}
}
}
TabView.xaml
<UserControl x:Class="Monitoring_Tool.Views.TabView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ProgressBar Value="{Binding Path=CurrentViewModel.PageProgress}" Height="5" Grid.Row="0" Margin="0,0,0,10">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderThickness="0,0,0,0" Background="LightGray" CornerRadius="0" Padding="0">
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#00B6FA" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ProgressBar.Style>
</ProgressBar>
<ContentControl Content="{Binding Path=CurrentViewModel}" Grid.Row="1" />
</Grid>
I instantiate the TabViewModel like this:
new TabViewModel("Producten", new ProductsViewModel())
The ProductsView.xaml is shown like it should be. In the ProductsView.xaml I call the command from the TabViewModel like this:
<DataGrid.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"/>
</DataGrid.InputBindings>
When the datagrid is empty, the command is executed and ProductView.xaml appears like it should be. But when the datagrid is not empty somthing strange happens:
the command is executed, and when I debug I can see that the currentViewModel is changed to ProductViewModel. Then when OnPropertyChanged("CurrentViewModel") is called. There is a set call (value = null) to a depedency property (SelectedAssetCategory) on the ProductsViewModel, wich was replaced and doesn't exits anymore?!
When I put CurrentViewModel = null the same thing happens, I can only do CurrentViewModel = new ProductsViewModel. So I guess it's somthing with updating the UI?
In the App.xaml I defined the following recources:
<DataTemplate DataType="{x:Type viewmodels:TabViewModel}">
<views:TabView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ProductsViewModel}">
<views:ProductsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ProductViewModel}">
<views:ProductView />
</DataTemplate>
The ProductsViewModel looks like this:
class ProductsViewModel: PageViewModel
{
private readonly MonitotingToolEntities _databaseEntities;
public ProductsViewModel()
{
_databaseEntities = new MonitotingToolEntities();
AssetCategories = new ObservableCollection<AssetCategory>(_databaseEntities.AssetCategory.ToList())
{
new AssetCategory() {AssetCategoryID = 0, AssetCategoryName = "Alles"}
};
Results = new ObservableCollection<Product>();
}
public ObservableCollection<AssetCategory> AssetCategories { get; set; }
private AssetCategory _selectedAssetCategory;
public AssetCategory SelectedAssetCategory
{
get { return _selectedAssetCategory; }
set
{
_selectedAssetCategory = value; //this one is called with value = null
OnPropertyChanged("SelectedAssetCategory");
Filter();
}
}
public ObservableCollection<Product> Results { get; set; }
public void Filter()
{
Results.Clear();
List<Product> products =
SelectedAssetCategory.AssetCategoryID == 0
? _databaseEntities.Product.ToList()
: SelectedAssetCategory.Product.ToList();
foreach (Product product in products)
{
Results.Add(product);
}
}
}
ProductsView.xaml:
<UserControl x:Class="Monitoring_Tool.Views.ProductsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:views="clr-namespace:Monitoring_Tool.Views"
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:viewModels="clr-namespace:Monitoring_Tool.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<CollectionViewSource x:Key="CvsAssetCategories" Source="{Binding Path= AssetCategories}" >
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="AssetCategoryID"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="CvsResults" Source="{Binding Path= Results}" >
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="AssetCategory.AssetCategoryName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style TargetType="Image" x:Key="ImageDisabledStyle">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid Margin="0, 0, 0, 10" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Asset Categorie:" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding Source={StaticResource CvsAssetCategories}}"
DisplayMemberPath="AssetCategoryName"
SelectedItem="{Binding SelectedAssetCategory}"
Margin="0,0,10,0"/>
<TextBlock Text="Zoeken:" Grid.Column="3" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox Grid.Column="4"
SelectedItem="{Binding SelectedSearchField}"
Margin="0,0,10,0"/>
<TextBox Text="{Binding Path=SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="5" Margin="0,0,10,0">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=SearchCommand}" CommandParameter="{Binding SearchQuery}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
<Button Grid.Column="6"
Command="{Binding SearchCommand}"
CommandParameter="{Binding SearchQuery}"
Padding="5,0,5,0" Margin="0,0,10,0" >
<Button.Content>
<Image Source="/Recourses/SearchIcon.png"
Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
</Button.Content>
</Button>
<Button Grid.Column="7"
Command="{Binding CancelSearchCommand}"
IsEnabled="{Binding CancelSearchEnabled}"
Padding="5,0,5,0">
<Button.Content>
<Image Source="/Recourses/CancelSearchIcon.png"
Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
</Button.Content>
</Button>
</Grid>
<DataGrid Name="DgProducts" AutoGenerateColumns="False"
RowHeaderWidth="0" Margin="0,0,0,10" Grid.Row="1" IsReadOnly="True"
SelectionMode="Single" CanUserReorderColumns="False"
EnableRowVirtualization="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
ItemsSource="{Binding Source={StaticResource CvsResults}}" SelectedItem="{Binding SelectedProduct}">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"
/>
</DataGrid.InputBindings>
<DataGrid.Resources>
<Style TargetType="DataGridColumnHeader" x:Key="DgVerticalColumnHeader">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270" />
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="LightGray"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey }"
Color="Black"/>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" Background="DarkGray" Padding="2,0,0,0"/>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Manager.ManagerName}" Header="Manager" />
<DataGridTextColumn Binding="{Binding Path=ProductName}" Header="Product" />
<DataGridTextColumn Binding="{Binding Path=MonitoringBy}" Header="Monitoring door" />
<DataGridTextColumn Binding="{Binding Path=AumProduct}" Header="AUM Product (mln)" />
<DataGridTextColumn Binding="{Binding Path=AumProductDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Product" />
<DataGridTextColumn Binding="{Binding Path=AumStrategy}" Header="AUM Strategie (mln)" />
<DataGridTextColumn Binding="{Binding Path=AumStrategyDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Strategie" />
<DataGridTextColumn Binding="{Binding Path=Aum}" Header="AUM (mln)" />
<DataGridTextColumn Binding="{Binding Path=TotalExpenseRatio}" Header="TER (bp)" />
<DataGridTextColumn Binding="{Binding Path=Fee}" Header="Total Fee" />
</DataGrid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Results.Count, StringFormat='{}{0} Producten'}" Grid.Column="0" Margin="0,0,0,0"/>
<Button Grid.Column="2"
Content="Toevoegen"
Padding="5,0,5,0" Margin="0,0,10,0"
Command="{Binding AddProductCommand}" />
<Button Grid.Column="3"
Content="Verwijderen"
Padding="5,0,5,0"
Command="{Binding Path=RemoveProductCommand}"
CommandParameter="{Binding Path=SelectedProduct}"/>
</Grid>
</Grid>
PageViewModel is an abstract class:
public abstract class PageViewModel: BaseViewModel
{
private int _pageProgress;
public int PageProgress
{
get { return _pageProgress; }
set
{
_pageProgress = value;
OnPropertyChanged("PageProgress");
}
}
}
It's kind of weird but it has something to do with the collectionviewsource (CvsAssetCategories) bound to the combobox. If I bind directly to the combobox without using the collectionviewsource the dependency property is not called. However, I like to use the collectionviewsource for the sortdescriptor. My solution now is a not null check on the setter from the dependency property, but I think its nasty way to go.
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?
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.
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.
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