How to show TreeView on ComboBox.SelectedItem != null - c#

I am writing a Directory explorer using the MVVM-Pattern.
My UI consists of a ComboBox which contains a path to a directory and a TreeView which acts as a Directory explorer.
The thing I am struggling with is to show the TreeView only when a ComboBox item is selected but I have no real idea how to achieve that.
Here is my code.
MainWindow.xaml
<StackPanel>
<TextBlock TextWrapping="Wrap" Text="Select a Repository Directory" Margin="10,0" FontSize="16"/>
<ComboBox x:Name="cmbx" Margin="10,30,10,0" ItemsSource="{Binding CmbxItems}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
<TreeView x:Name="FolderView" Height="250" Margin="10,50,10,0" ItemsSource="{Binding Items}" Visibility="{Binding IsItemSelected}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Name="img" Width="20" Margin="5"
Source="{Binding Type,
Converter={x:Static local:HeaderToImageConverter.ConverterInstance}}"/>
<TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
ViewModel.cs
/// <summary>
/// ViewModel for the main Directory view.
/// </summary>
class ViewModel : BaseViewModel
{
#region Properties
public ObservableCollection<DirectoryStructureViewModel> Items { get; set; }
public ObservableCollection<string> CmbxItems { get; set; }
public bool _itemIsSelected = false;
public bool ItemIsSelected
{
get
{
return _itemIsSelected;
}
set
{
_itemIsSelected = value;
}
}
public string _selectedItem;
public string SelectedItem
{
get
{
return _selectedItem;
}
set
{
ItemIsSelected = true;
MessageBox.Show("Selection changed");
_selectedItem = value;
}
}
#endregion
#region Constructor
/// <summary>
/// Constructor.
/// </summary>
public ViewModel()
{
CmbxItems = new ObservableCollection<string>(){};
CmbxItems.Add(DirectoryItem.rootPath);
//Get initial directory.
var children = DirectoryStructure.GetInitialDirectory();
//Create view models from data.
this.Items = new ObservableCollection<DirectoryStructureViewModel>(children.Select(dir => new DirectoryStructureViewModel(dir.FullPath, NodeTypes.Folder)));
}
#endregion
}
BaseViewModel.cs
/// <summary>
/// Base ViewModel that fires PropertyChanged events.
/// </summary>
public class BaseViewModel : INotifyPropertyChanged
{
//Event that is fired when aa child property changes its value.
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
DirectoryStructureViewModel.cs
public class DirectoryStructureViewModel : BaseViewModel
{
#region Properties
public NodeTypes Type { get; set; }
public string FullPath { get; set; }
public string Name { get { return DirectoryStructure.GetDirectoryOrFileName(this.FullPath); } }
/// <summary>
/// List of all children contained in this item.
/// </summary>
public ObservableCollection<DirectoryStructureViewModel> Children { get; set; }
/// <summary>
/// Indicates that this item can be expanded.
/// </summary>
public bool CanExpand { get { return this.Type != NodeTypes.File; } }
/// <summary>
/// Indicates if the current item is expanded.
/// </summary>
public bool IsExpanded
{
get
{
return this.Children?.Count(chldrn => chldrn != null) > 0;
}
set
{
if (value == true)
{
Expand();
}
else
{
this.ClearChildren();
}
}
}
public bool IsItemSelected { get; set; }
#endregion
#region Commands
public ICommand ExpandCommand { get; set; }
#endregion
#region Constructor
/// <summary>
/// Constructor.
/// </summary>
/// <param name="fullPath">Path of this item.</param>
/// <param name="type">Type of this item.</param>
public DirectoryStructureViewModel(string fullPath, NodeTypes type)
{
this.ExpandCommand = new TreeViewRelayCommand(Expand);
this.FullPath = fullPath;
this.Type = type;
this.ClearChildren();
}
#endregion
#region Helper Methods
//Removes all children from the list.
private void ClearChildren()
{
this.Children = new ObservableCollection<DirectoryStructureViewModel>();
if (this.Type != NodeTypes.File)
{
//Adds a dummy item to show the expand arrow.
this.Children.Add(null);
}
}
#endregion
#region Functions
/// <summary>
/// Expands this directory and finds all children.
/// </summary>
private void Expand()
{
if (this.Type != NodeTypes.File)
{
//Find all children
var children = DirectoryStructure.GetDirectoryContents(this.FullPath);
this.Children = new ObservableCollection<DirectoryStructureViewModel>(children.Select(content => new DirectoryStructureViewModel(content.FullPath, content.Type)));
}
else
{
return;
}
}
#endregion
}
Commands.cs
class TreeViewRelayCommand : ICommand
{
#region Members
private Action mAction;
#endregion
#region Events
/// <summary>
/// Event that is executed, when <see cref="CanExecute(object)"/> value has changed.
/// </summary>
public event EventHandler CanExecuteChanged = (sender, e) => { };
#endregion
#region Constructor
/// <summary>
/// Constructor.
/// </summary>
/// <param name="action"></param>
public TreeViewRelayCommand(Action action)
{
mAction = action;
}
#endregion
#region Command Methods
public bool CanExecute(object parameter)
{
return true;
}
/// <summary>
/// Executes the commands action.
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
mAction();
}
#endregion
}
Edit: I am using FodyWeavers

You are almost there:
<TreeView ... Visibility="{Binding IsItemSelected}">
One problem is that you are binding Visibility to bool. There should be an binding error in Outputs window (check it now and always for various possible problems with WPF applications).
You can use BoolToVisibilityConverter (using this answer):
<someContainer.Resources>
<BooleanToVisibilityConverter x:Key="converter" />
</someContainer.Resources>
...
<TreeView ... Visibility="{Binding IsItemSelected, Converter={StaticResource converter}}">
Backing fields shouldn't be public.
You should (normally) rise notifications for all properties used in bindings. Typically at the end of setter.
I'd personally use getter only property:
string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsItemSelected));
}
}
public bool IsItemSelected => SelectedItem != null;
Also you miss correct event rising method:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// can be public if you want to rise event from outside
protected void OnPropertyChanged([CallerMemberName] string property = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}

Related

WPF Data bindings not working after intiailize. No Binding Errors

I am having an issue with the data bindings between my View and ViewModel. The bindings only run at initialize and then they will not update. The code runs as expected and I have no binding errors per the output window, but the bindings on the UI will not update.
Program Setup:
WPF
C#
Prism V8.1.97
MVVM
.NET Framework 4.7.2
Things I have tried:
XAML set to bind directly to the property with INotifyPropertyChanged
XAML set to Find the RelativeSource of type UserControl
RelayCommand to update the UI, with and without Invoke to the main thread.
BackgroundWorker to update the UI, with and without Invoke to the main thread.
DelegateCommand to update the UI, with and without Invoke to the main thread.
i.Interaction.EventTriggers with Click to Invoke a UI update on the main thread.
Everyone of these will run the code, but will not update the UI. I have left some of the code that I have tried in the program for BackgroundWorker, Delegate void, and Dispatcher.BeginInvoke. I have written a few programs and have never had this issue. Did I setup something wrong when I created the program?
UPDATE: There seems to be an issue with Visual Studio 2019. This might be only on my version as the other programs I have written no longer work. This could would normally run as intended.
UserControl I am trying to do a simple binding with. I created a Textbox at the bottom with Mode=TwoWay to see if the TextBlock would update.
<UserControl x:Class="MRC.Module.ModStatus.Views.ModStatusManagerView"
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:MRC.Module.ModStatus.Views"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cconv="clr-namespace:MRC.Framework.Core.Data.Converters;assembly=MRC.Framework.Core"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" >
<UserControl.Resources>
<cconv:RemoveSpacesConverter x:Key="IntToStringConvert"/>
</UserControl.Resources>
<Grid>
<Grid.Background>
<SolidColorBrush Color="#222222"/>
</Grid.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderThickness="1" BorderBrush="DodgerBlue" VerticalAlignment="Stretch" Height="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}">
<StackPanel VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Height="20" Margin="0,6" Content="{Binding StartStop}" Width="100"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" >
<i:InvokeCommandAction Command="{Binding ProgressCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Margin="5,0" HorizontalAlignment="Center" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Health, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource IntToStringConvert}}" FontSize="16" Foreground="White"/>
<TextBlock Margin="5,0" Text="{Binding TestingText}" Foreground="White" FontSize="16" AutomationProperties.Name="TestText"/>
</StackPanel>
<!--
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Health, UpdateSourceTrigger=PropertyChanged}"
-->
<ProgressBar Maximum="100" Minimum="0"
VerticalAlignment="Bottom" Height="25" Margin="5" />
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.TestingText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="16" Height="30" Margin="5" Width="100"/>
</StackPanel>
</Border>
</Grid>
</Grid>
</Border>
<TextBlock HorizontalAlignment="Center" Grid.Column="1" VerticalAlignment="Center" Text="Mod Status"
FontSize="30" Foreground="Black"/>
</Grid>
</Grid>
</UserControl>
Code Behind
public partial class ModStatusManagerView : UserControl
{
public ModStatusManagerView()
{
InitializeComponent();
DataContext = new ModStatusManagerViewModel();
}
}
ViewModel
public class ModStatusManagerViewModel : ViewModelBase
{
#region Variables
private readonly BackgroundWorker worker = new BackgroundWorker();
private delegate void UpdateUIDelgate(string health, string Status);
#endregion
#region Commands
public ICommand ProgressCommand { get; private set; }
private void Testing(string health, string Status)
{
try
{
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private bool CanProgressExecute()
{
return true;
}
private void Progress()
{
try
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
Health = 0;
StartStop = "Stop";
TestingText = "Initialized";
for (int i = 0; i < 99; i++)
{
Health = i;
System.Windows.Application.Current.MainWindow.UpdateLayout();
System.Threading.Thread.Sleep(100);
}
TestingText = "Completed";
System.Windows.MessageBox.Show("Complete");
}, System.Windows.Threading.DispatcherPriority.Render);
/*if (!System.Windows.Application.Current.Dispatcher.CheckAccess())
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(
new UpdateUIDelgate(Testing), "Stop", "Initialized");
return;
}*/
// System.Windows.Application.Current.MainWindow.Dispatcher.BeginInvoke(
// new UpdateUIDelgate(Testing), "Stop", "Initialized");
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
public ICommand ProgressOffCommand { get; }
private void ProgressOff()
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
StartStop = "Start";
});
}
#endregion
#region Constructor
public ModStatusManagerViewModel()
{
this.ProgressCommand = new RelayCommand(Progress);
//this.ProgressOffCommand = new RelayCommand(ProgressOff);
}
#endregion
#region Properties
public bool IsEnabled
{
get { return _isEnabled; }
set { SetProperty(ref _isEnabled, value); }
}
private bool _isEnabled = true;
/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public string StartStop
{
get { return _startStop; }
set { SetProperty(ref _startStop, value); }
}
private string _startStop = "Start";
/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public bool IsOn
{
get { return _isOn; }
set { SetProperty(ref _isOn, value); }
}
private bool _isOn = false;
/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public double MaxHealth
{
get { return _maxHealth; }
set { SetProperty(ref _maxHealth, value); }
}
private double _maxHealth = 100;
/// <summary>
/// Gets or Sets the Min Health
/// </summary>
public double MinHealth
{
get { return _minHealth; }
set { SetProperty(ref _minHealth, value); }
}
private double _minHealth = 0;
/// <summary>
/// Gets or Sets the Min Health
/// </summary>
public double Health
{
get { return _Health; }
set { SetProperty(ref _Health, value); }
}
private double _Health = 0;
public string TestingText
{
get { return _testingText; }
set { SetProperty(ref _testingText, value); }
}
private string _testingText = "Waiting";
#endregion
#region Events
#endregion
#region Methods
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
while (IsOn)
{
Random rnd = new Random();
Health = rnd.Next(0, 100);
System.Threading.Thread.Sleep(150);
}
System.Threading.Thread.Sleep(150);
}
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
#endregion
}
In case someone would like to see the implementation of the INotifyPropertyChanged
public abstract class ViewModelBase : ModelBase
{
public ViewModelBase()
{
}
~ViewModelBase()
{
}
public bool HasAnyErrors { get; set; }
}
Here is the ModelBase that ViewModelBase Implements
public abstract class ModelBase : BindableBase, INotifyPropertyChanged
{
protected ModelBase()
{
}
~ModelBase() { }
public bool HasIssues { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public static event PropertyChangedEventHandler StaticPropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(
typeof(ViewModelBase),
new PropertyChangedEventArgs(propertyName));
}
}
Here is the RelayCommand
public class RelayCommand : ICommand
{
#region Private Members
private Action _action;
private Action<object> _actionOb;
Action<object> _execteMethod;
Func<object, bool> _canexecuteMethod;
#endregion
#region Public Events
/// <summary>
/// Basic Command
/// </summary>
public event EventHandler CanExecuteChanged = (sender, e) => { };
#endregion
#region Constructors
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="action"></param>
public RelayCommand(Action action)
{
_action = action;
}
/*public RelayCommand(System.Collections.ObjectModel.ObservableCollection<Frames.Model.Category> category, Action<object> action)
{
_actionOb = action;
}*/
/// <summary>
/// Default Constructor that passes an object
/// </summary>
/// <param name="execteMethod"></param>
/// <param name="canexecuteMethod"></param>
public RelayCommand(Action<object> execteMethod, Func<object, bool> canexecuteMethod)
{
_execteMethod = execteMethod;
_canexecuteMethod = canexecuteMethod;
}
/// <summary>
/// Default Constructor that determines if an action can execute before executing
/// </summary>
/// <param name="action"></param>
/// <param name="CanExecute"></param>
public RelayCommand(Action action, bool CanExecute)
{
if (CanExecute)
return;
_action = action;
}
public RelayCommand(Action<object> action, bool CanExecuteOb)
{
_actionOb = action;
}
#endregion
#region Command Methods
/// <summary>
/// Returns True if bool Parameter is not busy
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (parameter != null)
{
try
{
if ((bool)parameter)
return false;
return true;
}
catch
{
return true;
}
}
else
{
return true;
}
}
public bool CanExecuteOb(object parameter)
{
return true;
}
/// <summary>
/// Executes an Action that is not busy
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
Task.Run(async () =>
{
_action();
});
}
/// <summary>
/// Executes an Action that is not busy with a <href="Param">
/// </summary>
/// <param name="param"></param>
public void ExecuteOb(object param)
{
Task.Run(async () =>
{
_actionOb(param);
});
}
#endregion
}
UPDATE: It seems the functionality of SetProperty has stopped working.
I changed my properties from
public string TestingText
{
get { return _testingText; }
set { SetProperty(ref _testingText, value);}
}
private string _testingText = "Testing";
To
public string TestingText
{
get { return _testingText; }
set
{
if (_testingText == value)
return;
_testingText = value;
OnPropertyChanged("TestingText");
}
}
private string _testingText = "Testing";
everything is now working as it should be.

WPF OpenFileDialog using MVVM (Model-View-ViewModel) in c#

How to write WPF OpenFileDialog using MVVM (Model-View-ViewModel) in c#?
I have visited some websites regarding this OpenFileDialog ,but I didn't get clear idea about it. This is my code
In View.xaml:
<Window.... xmlns:VM="clr-namespace:myproject.myViewModel"
... >
<Window.DataContext><VM:myViewModel/>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding mygroup}" >
<ItemsControl.ItemTemplate >
<DataTemplate>
<Grid >....
<TextBlock Text="Color" Margin="20" VerticalAlignment="Center"/>
<ComboBox KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding Color}" />
<TextBlock Text="Shapes" Grid.Row="1" VerticalAlignment="Center" />
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Height="20" Width="150" SelectedIndex="0" HorizontalContentAlignment="Right"
VerticalAlignment="Center" ItemsSource="{Binding Shapes}">
</ComboBox>
<TextBlock Text="Open Files " VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" Margin="10" />
<TextBox Grid.Column="3" Text="" Height="30" Grid.Row="0" IsReadOnly="True"
TextAlignment="Right" VerticalContentAlignment="Center" Width="200" /> <Button Grid.Column="4" Content="Browse" Height="30" VerticalAlignment="Bottom" MinWidth="41" />
</Grid>
</Window>
In Model.cs:
namespace Myproject.Models
{
public class ProjectModel : INotifyPropertyChanged
{
private ObservableCollection<string> color;
private ObservableCollection<string> shapes;
public ObservableCollection<string> Color
{
get { return color; }
set
{
color = value;
NotifyPropertyChanged("Color");
}
}
public ObservableCollection<string> Shapes
{
get { return shapes; }
set
{
shapes = value;
NotifyPropertyChanged("Shapes");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
In ViewModel.cs:
namespace MyProject.ViewModels
{
public class myProjectViewModel : INotifyPropertyChanged
{
private ObservableCollection<myprojectmodel> mygroup;
public ObservableCollection<myprojectmodel> Mygroup
{
get => this.mygroup;
set
{
if (Equals(value, this.mygroup)) return;
this.mygroup = value;
OnPropertyChanged();
}
}
public ProjectViewModel()
{
Mygroup = new ObservableCollection<myprojectmodel>();
List<string> lstColor = new List<string>();
lstCity = new List<string> {"Pink", "Blue", "Red"};
List<string> lstShapes = new List<string>();
lstTemperature = new List<string> {"Rectangle", "Triangle", "Circle"};
Mygroup.Add(
new ProjectModel
{
Color= new ObservableCollection<string>(lstColor),
Shapes = new ObservableCollection<string>(lstShapes),
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
How should I write code to get OpenFileDialog.
I have seen this link WPF OpenFileDialog with the MVVM pattern? but I don't know how to write it for my above code.
please someone help me by editing my code to get OpenFileDailog.
In my opinion this kind of things doesn't belong in to the ViewModel. It's View specific logic. The View alone handles user input and then sends it to the ViewModel. ViewModel never asks the View to do something. This would invert the dependency chain and couple the ViewModel to the View. The dependencies have to be like this:
View --> ViewModel --> Model. The ViewModel doesn't even know about the type of views nor that there is a View at all.
You have to open the dialog from your view and then send the result to the view model.
For this you could create a simple event handler in your code-behind and attach it to a button's click event. You take the picked file and use an ICommand to invoke e.g. an open file action. That's MVVM (or MVP). Separate the concerns of the views from your models.
MainWindow.xaml:
<Window x:Class="WpfOpenDialogExample.OpenFileDialogSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OpenFileDialogSample" Height="300" Width="300">
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<Grid>
<Button Name="ShowFilePickerButton" Click="ShowFilePicker_OnClick" Content="Open file" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.IO;
using System.Windows;
using Microsoft.Win32;
namespace WpfOpenDialogExample
{
public partial class OpenFileDialogSample : Window
{
public OpenFileDialogSample()
{
InitializeComponent();
}
private void ShowFilePicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
OpenFileDialog openFileDialog = new OpenFileDialog();
if(openFileDialog.ShowDialog() == true && viewModel.OpenFileCommand.CanExecute(openFileDialog.FileName))
{
viewModel.OpenFileCommand.Execute(openFileDialog.FileName);
}
}
private void ShowFolderPicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
FolderBrowserDialog openFolderDialog = new FolderBrowserDialog();
if(openFolderDialog.ShowDialog() == DialogResul.Ok && viewModel.OpenFolderCommand.CanExecute(openFolderDialog.SelectedPath ))
{
viewModel.OpenFolderCommand.Execute(openFolderDialog.SelectedPath );
}
}
}
}
ViewModel.cs:
public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); }
private void OpenFile(string filePath)
{
...
}
private bool CanOpenFile(string filePath)
{
return File.Exists(filePath);
}
public ICommand OpenFolderCommand { get => new RelayCommand(OpenFolder, CanOpenFolder); }
private void OpenFolder(string folderPath)
{
...
}
private bool CanOpenFolder(string folderPath)
{
return Directory.Exists(filePath);
}
RelayCommand.cs:
using System;
using System.Windows.Input;
namespace WpfOpenDialogExample
{
/// <summary>
/// An implementation independent ICommand implementation.
/// Enables instant creation of an ICommand without implementing the ICommand interface for each command.
/// The individual Execute() an CanExecute() members are suplied via delegates.
/// <seealso cref="System.Windows.Input.ICommand"/>
/// </summary>
/// <remarks>The type of <c>RelaisCommand</c> actually is a <see cref="System.Windows.Input.ICommand"/></remarks>
public class RelayCommand : ICommand
{
/// <summary>
/// Default constructor to declare the concrete implementation of Execute(object):void and CanExecute(object) : bool
/// </summary>
/// <param name="executeDelegate">Delegate referencing the execution context method.
/// Delegate signature: delegate(object):void</param>
/// <param name="canExecuteDelegate">Delegate referencing the canExecute context method.
/// Delegate signature: delegate(object):bool</param>
public RelayCommand(Action<object> executeDelegate , Predicate<object> canExecuteDelegate)
{
this.executeDelegate = executeDelegate;
this.canExecuteDelegate = canExecuteDelegate;
}
/// <summary>
/// Invokes the custom <c>canExecuteDelegate</c> which should check wether the command can be executed.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
/// <returns>Expected to return tue, when the preconditions meet the requirements and therefore the command stored in <c>executeDelegate</c> can execute.
/// Expected to return fals when command execution is not possible.</returns>
public bool CanExecute(object parameter)
{
if (this.canExecuteDelegate != null)
{
return this.canExecuteDelegate(parameter);
}
return false;
}
/// <summary>
/// Invokes the custom <c>executeDelegate</c>, which references the command to execute.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
public void Execute(object parameter)
{
if (this.executeDelegate != null)
this.executeDelegate(parameter);
}
/// <summary>
/// The event is triggered every time the conditions regarding the command have changed. This occures when <c>InvalidateRequerySuggested()</c> gets explicitly or implicitly called.
/// Triggering this event usually results in an invokation of <c>CanExecute(object):bool</c> to check if the occured change has made command execution possible.
/// The <see cref="System.Windows.Input.CommandManager"/> holds a weakrefernce to the observer.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Action<object> executeDelegate;
private readonly Predicate<object> canExecuteDelegate;
}
}

CarouselView in XamarinForms

I'm trying to use the CarouselView in the Xamarin project. But I can’t do it. Here are the installed packages:
Here is the xaml code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FlowersStore"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
x:Class="FlowersStore.MainPage">
<StackLayout>
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height=".3*"/>
<RowDefinition Height=".7*"/>
</Grid.RowDefinitions>
<cv:CarouselView ItemsSource="{Binding Zoos}" x:Name="CarouselZoos">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.RowSpan="2" Aspect="AspectFill" Source="{Binding ImageUrl}"/>
<StackLayout Grid.Row="1" BackgroundColor="#80000000" Padding="12">
<Label TextColor="White" Text="{Binding Name}" FontSize="16" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/>
</StackLayout>
</Grid>
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
</Grid>
</StackLayout>
And here is the c # code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace FlowersStore
{
public class Zoo
{
public string ImageUrl { get; set; }
public string Name { get; set; }
}
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
LoadDataCatouselView();
}
public void LoadDataCatouselView()
{
ObservableCollection<Zoo> Zoos = new ObservableCollection<Zoo>
{
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/23c1dd13-333a-459e-9e23-c3784e7cb434/2016-06-02_1049.png",
Name = "Woodland Park Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/6b60d27e-c1ec-4fe6-bebe-7386d545bb62/2016-06-02_1051.png",
Name = "Cleveland Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/e8179889-8189-4acb-bac5-812611199a03/2016-06-02_1053.png",
Name = "Phoenix Zoo"
}
};
CarouselZoos.ItemsSource = Zoos;
}
}
}
I use Xamarin Live Player for debugging. The log on the mobile phone displays the following message:
[LogEntry: Time=19.11.2018 14:54:54 +03:00, Level=Error, Title=Visualization Error, Message=The given key was not present in the dictionary. (KeyNotFoundException)]
How to fix it? Thanks.
Update 1:
I replaced the code based on your advice. I used your advice. I tried to run the application on:
Androind version: 7.1
Emulator: Genymotion Galaxy S7 7.1.0 API 25
And got this error:
What it? :(
The problem is with the long path.
An easy solution is to move the entire project solution to a shorter path like C:\
Here's an explanation from microsoft:
Path Too Long Exception
In your XAML you have the following line:
<cv:CarouselView ItemsSource="{Binding Zoos}" x:Name="CarouselZoos">
This means that the code is looking to bind a property named Zoos to the ItemsSource property of the CarouselView. You will need to create a property of type List<View> and implement the INotifyPropertyChanged structure to update the view. You'll also need to assign the content page's BindingContext to itself (BindingContext = this;).
You may also find that you cannot simply bind a URL to the Image source, and expect the image to appear.
Add the BindingContext=this; after the InitializeComponent(); or else add CarouselZoos.ItemsSource = Zoos; in OnAppearing() method
Try this
First add this class, for property binding and implement the INotifyPropertyChanged structure to update the view.
public class ViewModelBase : INotifyPropertyChanged
{
string title = string.Empty;
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
string icon = string.Empty;
/// <summary>
/// Gets or sets the icon.
/// </summary>
/// <value>The icon.</value>
public string Icon
{
get { return icon; }
set { SetProperty(ref icon, value); }
}
bool isBusy;
/// <summary>
/// Gets or sets a value indicating whether this instance is busy.
/// </summary>
/// <value><c>true</c> if this instance is busy; otherwise, <c>false</c>.</value>
public bool IsBusy
{
get { return isBusy; }
set
{
SetProperty(ref isBusy, value);
}
}
/// <summary>
/// Sets the property.
/// </summary>
/// <returns><c>true</c>, if property was set, <c>false</c> otherwise.</returns>
/// <param name="backingStore">Backing store.</param>
/// <param name="value">Value.</param>
/// <param name="propertyName">Property name.</param>
/// <param name="onChanged">On changed.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
protected bool SetProperty<T>(
ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Occurs when property changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the property changed event.
/// </summary>
/// <param name="propertyName">Property name.</param>
protected void OnPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Now when you got the base class for bind properties you can add a view model class for bind the properties and follow the MVVM pattern. I think this is the most property way to manipulate the data.
public class Zoo
{
public string ImageUrl { get; set; }
public string Name { get; set; }
}
public class CarouselViewModel : ViewModelBase
{
private ObservableCollection<Zoo> zoos;
public ObservableCollection<Zoo> Zoos
{
get => zoos; set => SetProperty(ref zoos, value);
}
public CarouselViewModel()
{
zoos = new ObservableCollection<Zoo>
{
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/23c1dd13-333a-459e-9e23-c3784e7cb434/2016-06-02_1049.png",
Name = "Woodland Park Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/6b60d27e-c1ec-4fe6-bebe-7386d545bb62/2016-06-02_1051.png",
Name = "Cleveland Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/e8179889-8189-4acb-bac5-812611199a03/2016-06-02_1053.png",
Name = "Phoenix Zoo"
}
};
}
}
public partial class MainPage : ContentPage
{
public CarouselViewModel viewModel;
public MainPage()
{
InitializeComponent();
this.BindingContext = viewModel = new CarouselViewModel();
}
}

Text Box value not updated on source object Property changed

I am Binding TextBox Text with a view model property SelectedUser, but on update the controls associated with this property are not reflecting the changes.
My ViewModel & Model
public class UserViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged.members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private ObservableCollection<UserModel> _userList;
public ObservableCollection<UserModel> UserList
{
get
{
return _userList;
}
set
{
_userList = value;
}
}
private UserModel _selectedUser;
public UserModel SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged("SelectedUser");
}
}
public UserViewModel()
{
UserManager manager = new UserManager();
var FilterCombo = new List<ComboItem> {
new ComboItem{Text = "Name", Value = "Name"},
new ComboItem{Text = "Owner", Value = "Owner"},
new ComboItem{Text = "Email", Value = "Email"},
new ComboItem{Text = "Contact Number", Value = "Contact"},
new ComboItem{Text = "Address", Value = "Address"},
};
var filter = new FilterModel
{
FilterItems = FilterCombo,
FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
FilterValue = ""
};
_userList = new ObservableCollection<UserModel>(manager.GetList(filter));
}
}
public class UserModel :INotifyPropertyChanged
{
public Database.User user { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
SelectedUser property is getting updated on DataGrid Selection like following
<DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False"
HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
SelectedValue="{Binding SelectedUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
View content
<Label Content="Login ID" Grid.Row="1"/>
<TextBox Grid.Row="2" Text="{Binding SelectedUser.Login, Mode=TwoWay}" x:Name="txtLogin" />
<Label Content="Password" Grid.Row="3"/>
<TextBox Grid.Row="4" Text="{Binding SelectedUser.Password, Mode=TwoWay}" x:Name="txtPassword"/>
<Label Content="Full Name" Grid.Row="5"/>
<TextBox Grid.Row="6" Text="{Binding SelectedUser.Name, Mode=TwoWay}" x:Name="txtName"/>
<Label Content="Email" Grid.Row="7"/>
<TextBox Grid.Row="8" Text="{Binding SelectedUser.Email, Mode=TwoWay}" x:Name="txtEmail"/>
<Label Content="Contact" Grid.Row="9"/>
<TextBox Grid.Row="10" Text="{Binding SelectedUser.Contact, Mode=TwoWay}" x:Name="txtContact" />
Your code has multiple problems. Your UserModel class does not have the properties you are binding your xaml to (line Name, Email, Contact, ..). And you do not trigger the OnPropertyChanged method of INotifyPropertyChange for all properties you want to bind to. I recomend the following changes.
ViewModel base class
I would use the BindableBase class of PRISM as a base class for your viewmodels which have changing properties so you won't need to implement the INotifyPropertyChanged interface in every viewmodel.
/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.</param>
/// <returns>True if the value was changed, false if the existing value matched the
/// desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <typeparam name="T">The type of the property that has a new value</typeparam>
/// <param name="propertyExpression">A Lambda expression representing the property that has a new value.</param>
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
this.OnPropertyChanged(propertyName);
}
}
Model
Also I don't recomend binding neasted properties because your model class will get ugly because it needs to implement INotifyPropertyChanged. So if your Database.User class doesn't contain any logic or unwanted fields use it as model or create your own like so:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
public string Login { get; set; }
public string Email { get; set; }
public string Contact { get; set; }
public override string ToString()
{
return Name;
}
}
Extensions
To convert your Database.User to your model you can create an extension method
public static class DatabaseUserExtensions
{
public static User ToModel(this Database.User x)
{
//convert the object of type Database.User to your User model clas here
}
public static IEnumerable<User> ToModels(this IEnumerable<Database.User> xs)
{
return xs.Select(x => x.ToModel());
}
}
ViewModel
Because your model doesn't implement INotifyPropertyChanged you need to create wrappers for your properties. So change your viewModel to the following:
//use the BindableBase class as base class
public class UserViewModel : BindableBase
{
//use the correct Model class (the newly created or Database.User if it
//is sufficient).
//also IEnumerable is sufficient
//there is no need for an private field
public IEnumerable<User> UserList { get; private set; }
//wrapper for SelectedUser.Name
public string SelectedUserName
{
get { return SelectedUser.Name; }
set
{
SelectedUser.Name = value;
OnPropertyChanged(nameof(SelectedUserName));
}
}
//add wrappers for the other properties like
//SelectedUserPassword, SelectedUserContact, ...
//use the correct Model class
//(the newly created or Database.User if it is sufficient)
private User _selectedUser;
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
// call the OnPropertyChanged of BindableBase
// (I would also recommend using nameof)
OnPropertyChanged(nameof(SelectedUser));
// call OnPropertyChanged for the other wrapper properties like
// SelectedUserPassword, SelectedUserContact, ...
OnPropertyChanged(nameof(SelectedUserName));
// ...
}
}
public UserViewModel()
{
UserManager manager = new UserManager();
var FilterCombo = new List<ComboItem> {
new ComboItem{Text = "Name", Value = "Name"},
new ComboItem{Text = "Owner", Value = "Owner"},
new ComboItem{Text = "Email", Value = "Email"},
new ComboItem{Text = "Contact Number", Value = "Contact"},
new ComboItem{Text = "Address", Value = "Address"},
};
var filter = new FilterModel
{
FilterItems = FilterCombo,
FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
FilterValue = ""
};
//this uses now the extension method
//(your manager should be doing the converting and return the model
//instead of the database user if the database user class is
//not sufficient)
_userList = manager.GetList(filter).ToModels();
}
}
View
And all thoes properties you set in xaml aren't needed (Should be true by default).
<DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False"
HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
SelectedValue="{Binding Path=SelectedUser}">
and change the rest of your xaml according to the changes in the viewmodel
<Label Content="Login ID" Grid.Row="1"/>
<TextBox Grid.Row="2" Text="{Binding Path=SelectedUserLogin}" x:Name="txtLogin" />
<Label Content="Password" Grid.Row="3"/>
<TextBox Grid.Row="4" Text="{Binding Path=SelectedUserPassword}" x:Name="txtPassword"/>
<Label Content="Full Name" Grid.Row="5"/>
<TextBox Grid.Row="6" Text="{Binding Path=SelectedUserName}" x:Name="txtName"/>
<Label Content="Email" Grid.Row="7"/>
<TextBox Grid.Row="8" Text="{Binding Path=SelectedUserEmail}" x:Name="txtEmail"/>
<Label Content="Contact" Grid.Row="9"/>
<TextBox Grid.Row="10" Text="{Binding Path=SelectedUserContact}" x:Name="txtContact" />
First of all, your SelectedUser which is of type UserModel doesn't have all these properties (Login, Password, Name...), I guess your Database.User has them.
My easy solution for that is that you will bind all your View content DataContext to the SelectedUser and change the textboxe's bindings to - user.Login, user.Password ...

ComboBox Color Picker with LivePreview, set up itemssource at ComboBox

I like to build some ColorPicker app with help of ComboBox and LivePreview. I came a cross with already with first issue. I ComboBox I like to show rectangle with fill of selected color and textblock with name of the color. Colors will be manual choosen over RGB pallete.
My problem is that ComboBox dont show any of Color and no text. I attached code below, if any question please ask. I belive that my problem is in XAML code?
Now ComboBox show me only "ColorPickerWithLivePreview.ButtonIlluminationViewModel+ColorItem" - two lines of that because I have two colors in List.
ViewModel:
public class ButtonIlluminationViewModel : ViewModelBase
{
public string ButtonName
{
get
{
return "Button Illumination";
}
}
public ButtonIlluminationViewModel()
{
ColorList = new List<ColorItem>()
{
new ColorItem() { ColorName = "AppleGreen", Color = Color.FromArgb(255,255,255,255)},
new ColorItem() { ColorName = "AppleGreen", Color = Colors.Red },
};
}
public IList<ColorItem> ColorList
{
get;
private set;
}
public class ColorItem
{
public string ColorName
{
get;
set;
}
public Color Color
{
get;
set;
}
}
}
XAML:
<local:LivePreviewComboBox Grid.Column="1" Grid.Row="0" x:Name="liveBox" Width="200" SelectedIndex="0" ItemsSource="{Binding ColorList}" >
<local:LivePreviewComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Width="20" Height="20" Fill="{Binding }" Margin="1"/>
<TextBlock Height="20" Text="{Binding }" Margin="1"/>
</StackPanel>
</DataTemplate>
</local:LivePreviewComboBox.ItemTemplate>
</local:LivePreviewComboBox>
LivePreviewComboBox:
public class LivePreviewComboBox : ComboBox
{
#region DependencyProperty LivePreviewItem
/// <summary>
/// Gets or sets the live preview item.
/// </summary>
/// <value>The live preview item.</value>
public object LivePreviewItem
{
get { return GetValue(LivePreviewItemProperty); }
set { SetValue(LivePreviewItemProperty, value); }
}
/// <summary>
/// Dependency property to get or set the live preview item
/// </summary>
public static readonly DependencyProperty LivePreviewItemProperty =
DependencyProperty.Register("LivePreviewItem", typeof(object), typeof(LivePreviewComboBox),
new FrameworkPropertyMetadata(null));
#endregion
#region Construction
/// <summary>
/// Initializes a new instance of the <see cref="LivePreviewComboBox"/> class.
/// </summary>
public LivePreviewComboBox()
{
DependencyPropertyDescriptor.FromProperty(IsDropDownOpenProperty, typeof(LivePreviewComboBox))
.AddValueChanged(this, OnDropDownOpenChanged);
}
#endregion
#region Overrides
/// <summary>
/// See <see cref="ComboBox.OnSelectionChanged" />
/// </summary>
protected override DependencyObject GetContainerForItemOverride()
{
var container = base.GetContainerForItemOverride();
var comboBoxItem = container as ComboBoxItem;
if (comboBoxItem != null)
{
DependencyPropertyDescriptor.FromProperty(ComboBoxItem.IsHighlightedProperty, typeof(ComboBoxItem))
.AddValueChanged(comboBoxItem, OnItemHighlighted);
}
return container;
}
/// <summary>
/// See <see cref="ComboBox.OnSelectionChanged" />
/// </summary>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
LivePreviewItem = SelectedItem;
base.OnSelectionChanged(e);
}
#endregion
#region Private Helpers
private void OnItemHighlighted(object sender, EventArgs e)
{
var comboBoxItem = sender as ComboBoxItem;
if (comboBoxItem != null && comboBoxItem.IsHighlighted)
{
LivePreviewItem = comboBoxItem.DataContext;
}
}
private void OnDropDownOpenChanged(object sender, EventArgs e)
{
if (IsDropDownOpen == false)
{
LivePreviewItem = SelectedItem;
}
}
#endregion
}
You forgot to bind the Textblock Text and Rectangle Fill to the actual properties of the ColorItem.
You also need to bind the Fill property to a property of type Brush instead of Color. You can make a Brush from a color like this:
new SolidColorBrush(System.Windows.Media.Colors.Blue);

Categories

Resources