I'm new into WPF, and I'm unfortunately used to work only with WinForms.
I'd like to ask for advice with binding.
I want to fill menu items for languages like you can see in the picture below
I have obtained dictionary with language name as a key and bool as value in my viewModel:
internal Dictionary<string,bool> Languages { get; set; }
#endregion
public MainWindowViewModel()
{
Loc = new Locale(CurrentLanguage);
Languages = Loc.GetAvailableLanguages();
}
I have handled it by adding them in the code behind of the MainWindowView.xaml file:
public partial class MainWindow : Window
{
MainWindowViewModel viewModel;
public MainWindow()
{
viewModel = new MainWindowViewModel();
DataContext = viewModel;
InitializeComponent();
PopulateMILanguages();
}
private void PopulateMILanguages()
{
foreach (var lng in viewModel.Languages)
{
MenuItem mi = new MenuItem();
mi.IsCheckable = true;
mi.Header = lng.Key;
mi.Checked += Mi_Checked;
MI_Lngs.Items.Add(mi);
mi.IsChecked = lng.Value;
if (lng.Key.ToLowerInvariant() ==
Settings.Default.LastSelectedLanguage.ToLowerInvariant())
{
mi.IsChecked = true;
}
}
}
private void Mi_Checked(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
viewModel.CurrentLanguage = menuItem.Header.ToString()
.ToLowerInvariant();
Settings.Default.LastSelectedLanguage=viewModel.CurrentLanguage;
foreach (MenuItem mi in MI_Lngs.Items)
{
if (mi.Header != menuItem.Header)
{
mi.IsChecked = false;
}
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Settings.Default.Save();
}
}
I really don't like this solution, but I was not able to handle it to fill collection in the xaml:
<!-- LANGUAGE -->
<MenuItem x:Name="MI_Lngs" Header="{Binding Loc[Lng]}"
Style="{StaticResource MenuItemToolbar}">
<MenuItem.Icon>
<Image Source="/Assets/languages.png"/>
</MenuItem.Icon>
<MenuItem.ToolTip>
<ToolTip Content="{Binding Loc[Lng_TT]}"/>
</MenuItem.ToolTip>
</MenuItem>
Could anyone please give me an advice how to handle it to fill menu items from the collection and also be able to catch checked events?
Thanks in advance
Jiri
You can use this menu with the below viewmodel to get the desired functionality.
<Menu>
<MenuItem Header="File"/>
<MenuItem Header="Settings">
<MenuItem Header="Application"/>
<MenuItem Header="Language" ItemsSource="{Binding Languages}">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.IsCheckable" Value="True"/>
<Setter Property="MenuItem.Header" Value="{Binding Header, Mode=OneWay}"/>
<Setter Property="MenuItem.IsChecked" Value="{Binding IsSelected, Mode=OneWay}"/>
<Setter Property="MenuItem.Command" Value="{Binding DataContext.SelectLanguageCommand, RelativeSource={RelativeSource AncestorType=MenuItem}}"/>
<Setter Property="MenuItem.CommandParameter" Value="{Binding}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
</Menu>
I used CommunityToolkit.Mvvm library to give you some idea here. You can use another library.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace WpfApp1.ViewModel
{
public class MainWindowViewModel : ObservableObject
{
private ObservableCollection<Language> _languages = new();
public ObservableCollection<Language> Languages
{
get => _languages;
set => SetProperty(ref _languages, value);
}
public IRelayCommand SelectLanguageCommand { get; }
public MainWindowViewModel()
{
Languages.Add(new Language { Header = "CZ", IsSelected = false });
Languages.Add(new Language { Header = "EN", IsSelected = true });
SelectLanguageCommand = new RelayCommand<Language>(l =>
{
if (l != null)
{
foreach (Language item in Languages)
{
item.IsSelected = false;
}
l.IsSelected = true;
}
});
}
}
public class Language : ObservableObject
{
private string? _header;
public string? Header
{
get => _header;
set => SetProperty(ref _header, value);
}
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}
}
}
Related
Im using DevExpress and Prism in my application which Im new to both. Im trying to access "MyText" in the content item I create in my viewmodel. Currently when this item is created in CreatedSelectionEventReceived() via the viewmodel I don't see this property show up in my parameter list while debugging. I would like to know how to access this once its been created so I can update the property when a property is updated NumGaugeValue via the viewmodel. Right now I can hard code a value for "MyText" but unable to bind to it.
ResourceDictionary.xaml
<ResourceDictionary
x:Class="MyProject.WPF.Resources.CustomShapes"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"
xmlns:dxdiag="http://schemas.devexpress.com/winfx/2008/xaml/diagram"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxga="http://schemas.devexpress.com/winfx/2008/xaml/gauges"
xmlns:ctrl="clr-namespace:MyProject.WPF.Controls"
xmlns:vm="clr-namespace:MyProject.WPF.ViewModels">
<Style x:Key="NumGaugeContentItem" TargetType="dxdiag:DiagramContentItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ctrl:NumberGauge MyText="22"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MyView.xaml
<dxdiag:DiagramControl
x:Name="Diagram"
Width="{Binding ElementName=Dashboard, Path=ActualWidth}"
Height="{Binding ElementName=Dashboard, Path=ActualHeight}"
AllowAddRemoveItems="True"
AllowMoveItems="True"
AllowResizeItems="True"
CanvasSizeMode="Fill"
GridSize="25,25"
MaxZoomFactor="1"
MinZoomFactor="1"
ScrollMode="Content"
SelectedStencils="BasicShapes"
SelectionMode="Single"
ShowRulers="False"
ShowSelectionRectangle="False"
SnapToGrid="True"
SnapToItems="False"
ZoomFactor="1" />
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<prism:InvokeCommandAction Command="{Binding DiagramControlDropCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type dxdiag:DiagramControl}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
MyViewModel.cs
public class MyViewModel: BindableBase
{
public string NumGaugeValue
{
get { return _numGaugeValue; }
set { SetProperty(ref _numGaugeValue, value); }
}
private DiagramControl DiagramCtrl { get; set; }
private Point CtrlPlacementCoordinates { get; set; }
public DelegateCommand<string> UpdateCommand { get; set; }
public DelegateCommand<DragEventArgs> DiagramControlDropCommand { get; set; }
public MyViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
UpdateCommand = new DelegateCommand<string>(Execute).ObservesProperty(() => NumGaugeValue);
DiagramControlDropCommand = new DelegateCommand<DragEventArgs>(HandleDropOnDxDiagram);
ItemSelectionCommand = new DelegateCommand<object>(ProcessSelectionCommand);
_eventAggregator.GetEvent<ControlSelectionEvent>().Subscribe(ControlSelectionEventReceived);
}
public MyViewModel() { }
private DiagramContentItem CreateDiagramItem(string styleId, Point position)
{
var ctrlItem = new DiagramContentItem() {CustomStyleId = styleId, Position = position};
return ctrlItem;
}
private void HandleDropOnDxDiagram(DragEventArgs sender)
{
DiagramCtrl = (DiagramControl)sender.Source;
CtrlPlacementCoordinates = sender.GetPosition(DiagramCtrl);
}
private void DoDragDrop(object sender)
{
// Grab the control from the accordion item and store as the control that was dropped
var item = (AccordionItem)sender;
var controlDropped = (ControlType)item.Items[0];
if (controlDropped.DataContext != null)
{
DragDrop.DoDragDrop(controlDropped, controlDropped.DataContext, DragDropEffects.Move);
}
}
private void ControlSelectionEventReceived(ControlSelectionMessageInfo controlName)
{
// Create the control that was selected dynamically
switch (controlName.SelectedControlName)
{
case "NumberGauge":
var numGauge = CreateDiagramItem("NumGaugeContentItem", CtrlPlacementCoordinates);
DiagramCtrl.Items.Add(numGauge);
break;
//other cases here but not relevant
}
}
}
I'm working in MVVM, WPF and I have a popup; inside this popup is a listbox and inside the listbox I have a checkbox. The problem is: if I uncheck an item from the list box and click outside, popup disappears; if a I click again the checkbox is reseted at its initial value (all the items become checked).
So, how can I maintain the state of the popup set and stop its resetting while the app is running ? Can I do this through XAML ?
here is the code:
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked = false;
private T item;
public CheckedListItem()
{ }
public CheckedListItem(T item, bool isChecked)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
the viewModel:
private void OnApplyFiltersCommandRaised(object obj)
{
if (FilterElement.Contains("ClassView"))
{
switch (FilterElement)
{
case "buttonClassViewClassFilter":
FilteredClassViewItems.Clear();
FilteredFieldViewItems.Clear();
foreach (var filterItem in FilterItems)
{
if (filterItem.IsChecked == true)
{
FilteredClassViewItems.Add(classViewItems.First(c => c.ClassName == filterItem.Item));
FilteredFieldViewItems.Add(fieldViewItems.First(c => c.ClassName == filterItem.Item));
}
}
break;
...
public ObservableCollection<CheckedListItem<string>> FilterItems
{
get
{
return filterItems;
}
set
{
filterItems = value;
SetPropertyChanged("FilterItems");
}
}
the XAML part:
<ListBox x:Name="listBoxPopupContent"
Height="250"
ItemsSource="{Binding FilterItems}"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FontSize" Value="8" />
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Item}"
Command="{Binding DataContext.ApplyFiltersCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks in advance !
If you want to keep the state, you can just create a new view that will contain your listbox. Then your popup will be
<Popup>
<views:MyListBoxview>
</Popup>
where views is the path where wpf can find MyListBoxview.
This is an example of how you can do MyListBoxView. First of all, add a new usercontrol to your project. Then you create:
<ListBox ItemSource = {Binding MyCollectionOfItem}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked = {Binding IsItemChecked} Content = {Binding Name}/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will need to assign to this view a viewmodel that will of course implement INotifyPropertyChanged and that will have these this class defined inside it (also this class will implement INotifyPropertyChanged)
public class MyItem : INotifyPropertyChanged
{
public void SetPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private bool isItemChecked = false;
public bool IsItemChecked
{
get { return isItemChecked; }
set
{
isItemChecked = value;
SetPropertyChanged("IsItemChecked");
}
}
private string name ;
public string Name
{
get { return Name; }
set
{
name = value;
SetPropertyChanged("Name");
}
}
}
finally, the viewmodel that will represent the state of the popup will have inside this property
private ObservableCollection<MyItem> myCollectionOfItem = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> MyCollectionOfItem
{
get { return myCollectionOfItem; }
set
{
myCollectionOfItem = value;
SetPropertyChanged("MyCollectionOfItem");
}
}
I usually handle this kind of problem modelling properly the object that i need to bind to my controls in WPF
I am trying to bind a collection of custom objects to a tree view's ItemSource in WPF with no success.
Here is the MainWindow.xaml:
<Window
x:Class="AoiImageLift.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:AoiImageLift.UI.ViewModels"
Height="500"
Width="500">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<TreeView ItemsSource="{Binding TreeViewViewModel.ProcessList}"/>
</Window>
Here is the App.xaml:
</Application>
</Application.Resources>
<!-- TreeView Style -->
<Style TargetType="{x:Type TreeView}">
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
<Setter Property="SelectedValuePath" Value="Wafer"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding ProcessList}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock
FontFamily="SegoeUI"
Foreground="MidnightBlue"
Text="{Binding Wafer}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock
Text="{Binding ProcessNumber}"
FontFamily="SegoeUI"
Foreground="MidnightBlue"/>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
Here is the MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModel
{
private WaferSelectionTreeViewViewModel treeViewViewModel;
public MainWindowViewModel()
{
BackgroundWorker initThread = new BackgroundWorker();
initThread.DoWork += (sender, e) =>
{
e.Result = new SingulationOneTable().GetWaferList();
};
initThread.RunWorkerCompleted += (sender, e) =>
{
TreeViewViewModel = new WaferSelectionTreeViewViewModel(
(List<string>) e.Result);
};
initThread.RunWorkerAsync();
}
public WaferSelectionTreeViewViewModel TreeViewViewModel
{
get { return treeViewViewModel; }
set
{
treeViewViewModel = value;
OnPropertyChanged("TreeViewViewModel");
}
}
}
FYI, this line of code...
e.Result = new SingulationOneTable().GetWaferList();
...simply returns a large list of strings. That list of strings is then passed into the constructor of the WaferSelectionTreeViewViewModel class.
Here is the WaferSelectionTreeViewViewModel.cs:
public class WaferSelectionTreeViewViewModel : ViewModel
{
private ObservableCollection<Process> processList;
public class TreeViewItemBase : ViewModel
{
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
if (value != isSelected)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return isExpanded; }
set
{
if (value != isExpanded)
{
isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
}
}
public class Process : TreeViewItemBase
{
private string name;
public Process(string name)
{
this.name = name;
this.Children = new ObservableCollection<string>();
}
public string Name { get { return name; } }
public ObservableCollection<string> Children { get; set; }
}
public WaferSelectionTreeViewViewModel(List<string> waferList)
{
processList = new ObservableCollection<Process>();
List<string> procList = new List<string>();
foreach (string wafer in waferList)
{
procList.Add(wafer.Substring(0, 4));
}
IEnumerable<string> distintProcessList = procList.Distinct();
foreach (string process in distintProcessList)
{
Process newProcess = new Process(process);
List<string> wafersInProcess = waferList.FindAll(
x => x.Substring(0, 4) == process);
foreach (string waferInThisProcess in wafersInProcess)
{
newProcess.Children.Add(waferInThisProcess);
}
}
}
public ObservableCollection<Process> ProcessList
{
get
{
return processList;
}
set
{
processList = value;
OnPropertyChanged("ProcessList");
}
}
}
Can anyone figure out why the items are not showing up in the tree view??
Regards,
Kyle
There are a couple of errors in your bindings. If you run your application from Visual Studio, you'll probably see one or more messages like this in the Output window:
System.Windows.Data Error: 40 : BindingExpression path error: (...)
Firstly, each root item is bound to a Process object from TreeViewViewModel.ProcessList and it's told to display a property called ProcessNumber, but there is no such property, at least not in the code you posted. So I'm guessing the process items are showing up in the TreeView, but they're blank.
Secondly, your HierarchicalItemTemplate says that the list of child items can be found in a property called ProcessList. But each root item is a Process, which doesn't have that property, so no child items are displayed. You probably mean:
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
Now, Process.Children is a simple list of strings, so you don't need to include the <HierarchicalDataTemplate.ItemTemplate> part (and of course, strings don't have the Wafer property that template is looking for).
I am trying to create a File Explorer style TreeView/ListView window that will allow the user to select a "Project" from within a "ProjectFolder".
The "ProjectFolder" tree is bound to a property on my ViewModel called "RootProjectFolders" and the "Project" list is bound to a property called "ProjectsInSelectedFolder". Things were mostly working; however, I was getting null exceptions when I first loaded the window because the "SelectedFolder" had not yet been set. When I tried to implement a simple check to make sure that the "SelectedFolder" was not null, my "Project" ListView stopped refreshing.
if ((this.SelectedFolder != null) && (this.SelectedFolder.ProjectFolder.Projects != null))
{
foreach (Project project in this.SelectedFolder.ProjectFolder.Projects)
{
_projectsInSelectedFolder.Add(new ProjectViewModel(project));
}
}
base.RaisePropertyChangedEvent("ProjectsInSelectedFolder");
If I remove (this.SelectedFolder != null) from the above, the ListView will update, but I will get an NullException error. Why is that check breaking my binding?
Following up on the request for additional information, here is the XAML of the TreeView and ListView that are binding to the properties on the ViewModel:
<TreeView Name="treeviewProjectFolders" Grid.Column="0"
ItemsSource="{Binding Path=RootProjectFolders}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<GridSplitter Name="splitterProjects" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<ListView Name="listviewProjects" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Path=ProjectsInSelectedFolder}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is the ViewModel
public class SelectProjectViewModel : ViewModelBase
{
#region Fields
List<ProjectViewModel> _projectsInSelectedFolder;
List<ProjectFolderViewModel> _rootProjectFolders;
static ProjectFolderViewModel _selectedFolder = null;
ProjectViewModel _selectedProject;
#endregion // Fields
#region Constructor
public SelectProjectViewModel(ProjectFolders rootProjectFolders)
{
if (_rootProjectFolders != null) { _rootProjectFolders.Clear(); }
_rootProjectFolders = new List<ProjectFolderViewModel>();
foreach (ProjectFolder rootFolder in rootProjectFolders)
{
_rootProjectFolders.Add(new ProjectFolderViewModel(rootFolder, this));
}
_projectsInSelectedFolder = new List<ProjectViewModel>();
// Subscribe to events
this.PropertyChanged += OnPropertyChanged;
}
#endregion // Constructor
#region Properties
public List<ProjectFolderViewModel> RootProjectFolders
{
get
{
return _rootProjectFolders;
}
}
public List<ProjectViewModel> ProjectsInSelectedFolder
{
get
{
return _projectsInSelectedFolder;
}
}
public ProjectFolderViewModel SelectedFolder
{
get
{
return _selectedFolder;
}
set
{
if (_selectedFolder != value)
{
_selectedFolder = value;
}
}
}
public ProjectViewModel SelectedProject
{
get
{
return _selectedProject;
}
set
{
_selectedProject = value;
base.RaisePropertyChangedEvent("SelectedProject");
}
}
#endregion // Properties
#region Methods
public void FindSelectedFolder(ProjectFolderViewModel root)
{
if (root.IsSelected) { _selectedFolder = root; }
else
{
foreach (ProjectFolderViewModel folder in root.Children)
{
if (_selectedFolder == null)
{
FindSelectedFolder(folder);
}
}
}
}
#endregion // Methods
#region Event Handlers
void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "SelectedFolder":
_selectedFolder = null;
foreach (ProjectFolderViewModel root in this.RootProjectFolders)
{
if (_selectedFolder == null)
{
this.FindSelectedFolder(root);
}
}
_projectsInSelectedFolder.Clear();
if ((this.SelectedFolder != null) && (this.SelectedFolder.ProjectFolder.Projects != null))
{
foreach (Project project in this.SelectedFolder.ProjectFolder.Projects)
{
_projectsInSelectedFolder.Add(new ProjectViewModel(project));
}
}
base.RaisePropertyChangedEvent("ProjectsInSelectedFolder");
break;
}
}
#endregion // Event Handlers
Also, here is the ViewModel for the individual project folders that are used to raise the "SelectedFolder" property:
public class ProjectFolderViewModel : ViewModelBase
{
#region Fields
ReadOnlyCollection<ProjectFolderViewModel> _children;
List<ProjectFolderViewModel> _childrenList;
bool _isExpanded;
bool _isSelected;
ProjectFolderViewModel _parentNode;
SelectProjectViewModel _parentTree;
ProjectFolder _projectFolder;
#endregion // Fields
#region Constructor
public ProjectFolderViewModel(ProjectFolder projectFolder, SelectProjectViewModel parentTree) : this(projectFolder, parentTree, null)
{ }
private ProjectFolderViewModel(ProjectFolder projectFolder, SelectProjectViewModel parentTree, ProjectFolderViewModel parentNode)
{
_projectFolder = projectFolder;
_parentTree = parentTree;
_parentNode = parentNode;
_childrenList = new List<ProjectFolderViewModel>();
foreach (ProjectFolder child in _projectFolder.ChildFolders)
{
_childrenList.Add(new ProjectFolderViewModel(child, _parentTree));
}
_children = new ReadOnlyCollection<ProjectFolderViewModel>(_childrenList);
}
#endregion // Constructor
#region Properties
public ReadOnlyCollection<ProjectFolderViewModel> Children
{
get
{
return _children;
}
}
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
if (value != _isExpanded)
{
_isExpanded = value;
this.OnPropertyChanged("IsExpanded");
}
// Expand all the way up to the root.
if (_isExpanded && _parentNode != null)
_parentNode.IsExpanded = true;
}
}
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
base.RaisePropertyChangedEvent("IsSelected");
//if (_isSelected)
//{
_parentTree.RaisePropertyChangedEvent("SelectedFolder");
//}
}
}
public string Name
{
get
{
return _projectFolder.Name;
}
}
public ProjectFolder ProjectFolder
{
get
{
return _projectFolder;
}
}
#endregion // Properties
Change all your
List<T> to observablecollection<T>
because when ever there is new file or folder your adding the Item, your not creating new List, since observablecollection implements INotifyCollectionChanged, and INotifyPropertyChanged it'll internally take care of notifying and refreshing the View. But list cant do that
This might be simple for you guys but am just starting in WPF, and always my mind thinks in terms of Winforms, and am wrong all the time.
Anyway here is my situation. I have label in my view like below:
UserControl
<UserControl.Resources>
<Converters:BooleanToVisibilityConverter x:Key="visibilityConverter"></Converters:BooleanToVisibilityConverter>
<!-- Error Handling -->
<Converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Converters:ErrorConverter x:Key="errorConverter"/>
<ControlTemplate x:Key="ErrorTemplate">
<Border BorderBrush="Red" BorderThickness="2">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors), Converter={StaticResource errorConverter}}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="comboBoxInError" TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors), Converter={StaticResource errorConverter}}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
Label
<Label Name="IsImageValid" Content="Image Created" Margin="0,7,-1,0" Style="{StaticResource LabelField}"
Grid.ColumnSpan="2" Grid.Row="15" Width="90" Height="28" Grid.RowSpan="2"
Grid.Column="1" IsEnabled="True"
Visibility="{Binding IsImageValid,Converter={StaticResource BooleanToVisibilityConverter}}" />
I am trying to call the this label in my view model but not sure how.
I have t following method in the viewmodel, planning to use the label to display some message based on the condition like below.
ViewModel
public class MetadataViewModel : NotificationObject, IMetadataViewModel
{
#region :: Properties ::
private IEventAggregator eventAggregator;
private IImageResizerService imageResizer;
private string headerInfo;
public string HeaderInfo
{
get
{
return headerInfo;
}
set
{
if (this.headerInfo != value)
{
this.headerInfo = value;
this.RaisePropertyChanged(() => this.HeaderInfo);
}
}
}
public ICommand SaveCommand
{
get;
private set;
}
public ICommand CloseCommand
{
get;
private set;
}
public ICommand DeleteCommand
{
get;
private set;
}
public ICommand SubmitCommand
{
get;
private set;
}
public ICommand UnSubmitCommand
{
get;
private set;
}
public ICommand LocationSearchCommand
{
get;
private set;
}
public ICommand SubjectSearchCommand
{
get;
private set;
}
public ICommand RemoveLocationCommand
{
get;
private set;
}
public ICommand RemoveSubjectCommand
{
get;
private set;
}
private StoryItem selectedStory;
public StoryItem SelectedStory
{
get
{
return this.selectedStory;
}
set
{
if (this.selectedStory != value)
{
this.selectedStory = value;
this.RaisePropertyChanged(() => this.SelectedStory);
// raise dependencies
this.RaisePropertyChanged(() => this.CanSave);
this.RaisePropertyChanged(() => this.CanUnSubmit);
this.RaisePropertyChanged(() => this.CanDelete);
}
}
}
public List<Program> ProgramList
{
get;
private set;
}
public List<Genre> GenreList
{
get;
private set;
}
public List<Copyright> CopyrightList
{
get;
private set;
}
public bool CanSave
{
get
{
bool canSave = false;
if (this.SelectedStory.IsLockAvailable)
{
if (!this.SelectedStory.Submitted)
{
canSave = true;
}
}
return canSave;
}
}
public bool CanDelete
{
get
{
bool canDelete = false;
if (this.SelectedStory.IsLockAvailable)
{
if (!this.SelectedStory.Submitted)
{
canDelete = true;
}
}
return canDelete;
}
}
public bool CanUnSubmit
{
get
{
bool canUnSubmit = false;
if (this.SelectedStory.IsLockAvailable)
{
if (this.SelectedStory.Submitted)
{
canUnSubmit = true;
}
}
return canUnSubmit;
}
}
#endregion
#region :: Contructor ::
[ImportingConstructor]
public MetadataViewModel(
IMetadataController metadataController,
IGatewayService gateway,
INavigationService navigator,
IImageResizerService imageResizer,
IEventAggregator eventAggregator
)
{
this.eventAggregator = eventAggregator;
this.imageResizer = imageResizer;
// populate drop-down lists
this.ProgramList = gateway.GetPrograms(true);
this.GenreList = gateway.GetGenres();
this.CopyrightList = gateway.GetCopyrights();
// add dummy values so the user can de-select
this.ProgramList.Add(new Program());
this.GenreList.Add(new Genre());
this.CopyrightList.Add(new Copyright());
// commands
this.SaveCommand = metadataController.SaveCommand;
this.CloseCommand = metadataController.CloseCommand;
this.DeleteCommand = metadataController.DeleteCommand;
this.SubmitCommand = metadataController.SubmitCommand;
this.UnSubmitCommand = metadataController.UnSubmitCommand;
this.LocationSearchCommand = new DelegateCommand<string>(this.LocationSearch);
this.SubjectSearchCommand = new DelegateCommand<string>(this.SubjectSearch);
this.RemoveLocationCommand = new DelegateCommand<Topic>(this.RemoveLocation);
this.RemoveSubjectCommand = new DelegateCommand<Topic>(this.RemoveSubject);
// events
this.eventAggregator.GetEvent<StorySelectedEvent>().Subscribe(OnStorySelected, ThreadOption.UIThread);
this.eventAggregator.GetEvent<AddLocationEvent>().Subscribe(OnAddLocation, ThreadOption.UIThread);
this.eventAggregator.GetEvent<AddSubjectEvent>().Subscribe(OnAddSubject, ThreadOption.UIThread);
this.eventAggregator.GetEvent<CommandCompletedEvent>().Subscribe(OnCommandCompleted, ThreadOption.UIThread);
this.eventAggregator.GetEvent<ImageResizeCompletedEvent>().Subscribe(OnImageResizeCompleted, ThreadOption.UIThread);
this.Initialize();
}
#endregion
private void OnStorySelected(StoryItem selectedStory)
{
if (this.selectedStory != null)
{
this.Initialize();
// override the initialized values
this.SelectedStory = selectedStory;
this.SelectedStory.HaveChanged = false;
this.HeaderInfo = "Edit";
}
}
public void OnAddLocation(Topic topic)
{
if (topic != null)
{
if (!this.SelectedStory.Locations.Contains(topic))
{
this.SelectedStory.Locations.Add(topic);
this.RaisePropertyChanged(() => this.SelectedStory.Locations);
}
}
}
public void OnAddSubject(Topic topic)
{
if (topic != null)
{
if (!this.SelectedStory.Subjects.Contains(topic))
{
this.SelectedStory.Subjects.Add(topic);
this.RaisePropertyChanged(() => this.SelectedStory.Subjects);
}
}
}
private void OnCommandCompleted(string commandType)
{
if (commandType == CommandTypes.MetadataEntry)
{
this.Initialize();
}
}
private void OnImageResizeCompleted(bool isSuccessful)
{
IsImageValid = false;
if (isSuccessful)
{
this.SelectedStory.KeyframeImages = true;
IsImageValid = true;
}
else
{
this.SelectedStory.KeyframeImages = false;
IsImageValid=false;
}
}
private void Initialize()
{
this.SelectedStory = new StoryItem();
this.HeaderInfo = "Create";
}
private void LocationSearch(object topicType)
{
this.eventAggregator.GetEvent<LocationSearchEvent>().Publish(null);
}
private void SubjectSearch(object topicType)
{
this.eventAggregator.GetEvent<SubjectSearchEvent>().Publish(null);
}
private void RemoveLocation(Topic selected)
{
if (selected != null)
{
// remove the primary too
if (this.SelectedStory.PrimaryLocation != null)
{
if (string.Equals(this.SelectedStory.PrimaryLocation.FullName, selected.FullName, StringComparison.InvariantCultureIgnoreCase))
{
this.SelectedStory.PrimaryLocation = new Topic();
}
}
bool isSuccessful = this.SelectedStory.Locations.Remove(selected);
if (isSuccessful)
{
this.RaisePropertyChanged(() => this.SelectedStory.Locations);
}
}
}
private void RemoveSubject(Topic selected)
{
if (selected != null)
{
// remove the primary too
if (this.SelectedStory.PrimarySubject != null)
{
if (string.Equals(this.SelectedStory.PrimarySubject.FullName, selected.FullName, StringComparison.InvariantCultureIgnoreCase))
{
this.SelectedStory.PrimarySubject = new Topic();
}
}
bool isSuccessful = this.SelectedStory.Subjects.Remove(selected);
if (isSuccessful)
{
this.RaisePropertyChanged(() => this.SelectedStory.Subjects);
}
}
}
}
private booly _isImageValid;
public bool IsImageValid
{
get
{
return _isImageValid;
}
set
{
_isImageValid = value;
this.RaisePropertyChanged(() => this.IsImageValid);
}
}
}
Honestly i don't know how view will understand the binding.
A standard approach is to have a boolean property like "IsImageValid" in your ViewModel...Then in your XAML, bind the Visibility property of your label to that property, with a BooleanToVisibilityConverter http://msdn.microsoft.com/en-us/library/system.windows.controls.booleantovisibilityconverter.aspx
<UserControl.Resources>
<BooleanToVisibilityConverter
x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
Then use it in one or more bindings like this:
<Label Visibility="{Binding IsImageValid,
Converter={StaticResource BooleanToVisibilityConverter}}"
......... />
please read this post first.
If you wanna display some text in your Label then your have to do the following steps:
add a Property to your Viewmodel
implement INotifyPropertyChanged in your Viewmodel and raise the event when ever the Property change
in your view set the DataContext to your Viewmodel instance
create a Binding in xaml to your property
thats all ;)
You don't have to see View from ViewModel, it is a basic principle behind MVVM pattern. View knows about VM, whereas VM does not know about View. Instead, you can do as #JeffN825 suggested, at least I'd recommend this as well.
Add the following to your user Control Property:
xmlns:VM="clr-namespace:<ProjectName>.ViewModels" //this place throws exception,what is <ProjectName> ?
For assigning the DataContext to the User Control use the following code if the DataContext is not already assigned:
<UserControl.DataContext> //where to add this part ?
<VM:MyViewModel>
</UserControl.DataContext>
Bind the Visibility of the label in the following manner:
Visibility="{Binding IsImageValid}" //this is done
Your ViewModel or ViewModelBase from which the VM is inherited should implement the INotifyPropertyChanged Interface:
namespace MyApp.ViewModels //this i have to do it at xaml.cs file or suppose to be in viewmodel ?
{
public class MyViewModel : INotifyPropertyChanged
{...
...
}
}
Declare the data member and Property in your VM like this:
private System.Windows.Visibility _isImageValid; //add this code in my viewmodel
public System.Windows.Visibility IsImageValid
{
get
{
return _isImageValid;
}
set
{
_isImageValid = value;
this.RaisePropertyChanged(() => this.IsImageValid);
}
}