How to change UI language using resource dictionary at run time? - c#

I want to change my project language by button_click event, so I use ResourceDictionary to do it like this:
XAML
<Button Content="{DynamicResource LanguageSetting}" Click="btn_LanguageSetting_Click"/>
Code Behind
public static string windowCurrentLanguageFile = "Language/en.xaml";
private void btn_LanguageSetting_Click(object sender, RoutedEventArgs e)
{
windowCurrentLanguageFile = windowCurrentLanguageFile == "Language/en.xaml"
? "Language/fr.xaml"
: "Language/en.xaml";
var rd = new ResourceDictionary() { Source = new Uri(windowCurrentLanguageFile, UriKind.RelativeOrAbsolute) };
if (this.Resources.MergedDictionaries.Count == 0)
this.Resources.MergedDictionaries.Add(rd);
else
this.Resources.MergedDictionaries[0] = rd;
}
This works fine for the xaml file, but I also want to change my viewmodel's language in the code behind.
My ItemsControl in the xaml:
<ItemsControl ItemsSource="{Binding ItemOperate}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewmodel:SelectableViewModel}">
<Border x:Name="Border" Padding="0,8,0,8" BorderThickness="0 0 0 1" BorderBrush="{DynamicResource MaterialDesignDivider}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Checkerz" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton VerticalAlignment="Center" IsChecked="{Binding IsSelected}"
Style="{StaticResource MaterialDesignActionLightToggleButton}"
Content="{Binding Code}" />
<StackPanel Margin="8 0 0 0" Grid.Column="7">
<TextBlock FontWeight="Bold" Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" />
</StackPanel>
</Grid>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource MaterialDesignSelection}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Which Binding to the ViewModel like this:
public class SelectableViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
_isSelected = value;
OnPropertyChanged();
}
}
private char _code;
public char Code
{
get { return _code; }
set
{
if (_code == value) return;
_code = value;
OnPropertyChanged();
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
OnPropertyChanged();
}
}
private string _description;
public string Description
{
get { return _description; }
set
{
if (_description == value) return;
_description = value;
OnPropertyChanged();
}
}
}
And
public MainViewModel()
{
_itemOperate = CreateData();
}
private static ObservableCollection<SelectableViewModel> CreateData()
{
return new ObservableCollection<SelectableViewModel>
{
new SelectableViewModel
{
Code = 'E',
Name = "Erase",
Description = "Erase The MCU Chip By Page"
},
new SelectableViewModel
{
Code = 'D',
Name = "Detect",
Description = "Detect The MCU Flash",
},
new SelectableViewModel
{
Code = 'P',
Name = "Programming",
Description = "Programming The MCU Chip By Hex File",
},
new SelectableViewModel
{
Code = 'V',
Name = "Verify",
Description = "Verify The Downing Code",
},
new SelectableViewModel
{
Code ='L',
Name = "Lock",
Description = "Lock The Code To Protect The MCU",
}
};
}
So how I suppose to change the language my model SelectableViewModel Code, Name, Description instead of using this hard code? Thanks!

I will propose you a solution for changing the language at runtime.
Create the resource manager:
public class CultureResources
{
private static bool isAvailableCulture;
private static readonly List<CultureInfo> SupportedCultures = new List<CultureInfo>();
private static ObjectDataProvider provider;
public CultureResources()
{
GetAvailableCultures();
}
/// <summary>
/// Gets automatically all supported cultures resource files.
/// </summary>
public void GetAvailableCultures()
{
if (!isAvailableCulture)
{
var appStartupPath = AppDomain.CurrentDomain.BaseDirectory;
foreach (string dir in Directory.GetDirectories(appStartupPath))
{
try
{
DirectoryInfo dirinfo = new DirectoryInfo(dir);
var culture = CultureInfo.GetCultureInfo(dirinfo.Name);
SupportedCultures.Add(culture);
}
catch (ArgumentException)
{
}
}
isAvailableCulture = true;
}
}
/// <summary>
/// Retrieves the current resources based on the current culture info
/// </summary>
/// <returns></returns>
public Resources GetResourceInstance()
{
return new Resources();
}
/// <summary>
/// Gets the ObjectDataProvider wrapped with the current culture resource, to update all localized UIElements by calling ObjectDataProvider.Refresh()
/// </summary>
public static ObjectDataProvider ResourceProvider
{
get {
return provider ??
(provider = (ObjectDataProvider)System.Windows.Application.Current.FindResource("Resources"));
}
}
/// <summary>
/// Changes the culture
/// </summary>
/// <param name="culture"></param>
public void ChangeCulture(CultureInfo culture)
{
if (SupportedCultures.Contains(culture))
{
Resources.Culture = culture;
ResourceProvider.Refresh();
}
else
{
var ci = new CultureInfo("en");
Resources.Culture = ci;
ResourceProvider.Refresh();
}
}
/// <summary>
/// Sets english as default language
/// </summary>
public void SetDefaultCulture()
{
CultureInfo ci = new CultureInfo("en");
Resources.Culture = ci;
ResourceProvider.Refresh();
}
/// <summary>
/// Returns localized resource specified by the key
/// </summary>
/// <param name="key">The key in the resources</param>
/// <returns></returns>
public static string GetValue(string key)
{
if (key == null) throw new ArgumentNullException();
return Resources.ResourceManager.GetString(key, Resources.Culture);
}
/// <summary>
/// Sets the new culture
/// </summary>
/// <param name="cultureInfo"> new CultureInfo("de-DE"); new CultureInfo("en-gb");</param>
public void SetCulture(CultureInfo cultureInfo)
{
//get language short format - {de} {en}
var ci = new CultureInfo(cultureInfo.Name.Substring(0,2));
ChangeCulture(ci);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
// CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-GB");
}
}
Define resource file for each language type as you want.
above I've defined the default for English and the second file for German (you can see the ".de" extension. So you do for any other language.
Be sure that you open the Resources.resx properties and select as Custom Tool the value PublicResXFileCodeGenerator. This step is necessary in order to expose the Resource through an object provider.
Register the Resource provider via an object provider in the App.xaml:
Usage:
in Xaml :
Text="{Binding ResourcesText1, Source={StaticResource Resources}}"
From *.cs : Resources.ResourcesText1.
P.S.
When you change the culture be sure you call SetCulture method from the CultureResouces.

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, Datagrid with restricted input

recently I have started a new project in c#- Fireworks planning software. I am quite experienced in WinForms, but I want to learn WPF. So I have passed a couple of tutorials, but still on very beginning.
The task is to make a datagrid, where user puts data for each Firing point, thus for instance
Cue, EffectTime, DelayBeforeEffect, FiringTime and Name (it is not complete). Main window preview
No the thing is the time (EffectTime etc.) has to be input by user via keyboard and I want to:
if the value is like sss.fff (simple number or decimal number)
the program converts it automatically to TimeSpan (h:mm:ss.fff),
which is actually resolution of My firing system. Example: number
65.528 is converted to 0:01:05.528.
negative values are also allowed, because there are several
things have to be done before main pyrotechnics show starts
I would like validate user input it is valid time value, not for
instance 65.528.20. If the value is not correct I need either cancel
user leave the editedcell or at least rewrite the new value with
last validated
there is some relationship between values, for instance: FiringPoint
= EffectTime - Delay, or better FiringPoint = EffectTime.Subtract(Delay);
As I am used to I have created a UserControl TimeText with only one element - TextBox and I check, eventually correct, input upon points listed above.
Then I have created DataGrid and DataGridTemplateColumn in MainWindow,
classes like
FiringPoint (with properties listeda above),
FiringPointManager with ObservableCollection<FiringPoint> implementing INotifyChange interface
Data Binding between Manager and DataGrid.
After facing VisualStudio for about 20 hours, it is really working, but it seems to be very complicated.
Because for instance I need to forward DataGridRow.Background color to my TimeText control via dependencyProperty etc.
So finally my question: Is my approach correct or can somebody suggest better approach how to reach the same functionality?
Thanks for your opinions
Jan
Code for TimeTextBox
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DataGridHratky
{
/// <summary>
/// Interakční logika pro TimeTextBox.xaml
/// </summary>
public partial class TimeTextBox : UserControl, INotifyPropertyChanged
{
#region Overrides
#endregion
#region Enum
#endregion
#region constants
private const string __NEG = "-";
private static readonly string[] __SEP = { ".", "," };
public const string __TFORMAT = #"%h\:mm\:ss\.fff";
private const string __COL = ":";
#endregion
#region private declarations
private bool _isNegative = false;
private double _onlySecondsValue = 0;
private Brush _okBackground;
private Brush _nokBackground = Brushes.OrangeRed;
private Brush _negBackground = Brushes.LavenderBlush;
private TimeSpan _tsValue = new TimeSpan();
private bool _bOnlySeconds = false;
private string _originalValue;
#endregion
#region private methods
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Determines if value is only in second format. May be also negative and decimal either with "." or ","
/// </summary>
/// <param name="text">text to check</param>
/// <param name="seconds">Out variable, where seconds should be stored</param>
/// <returns>true if so</returns>
private bool onlySeconds(string text, out double seconds)
{
if (Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator == __SEP[0])
text = text.Replace(__SEP[1], __SEP[0]);
else text = text.Replace(__SEP[0], __SEP[1]);
if (Double.TryParse(text, out seconds))
{
_bOnlySeconds = true;
return true;
} else
{
_bOnlySeconds = false;
return false;
}
}
private bool validate(string time)
{
string _rawValue = time;
if (onlySeconds(_rawValue, out _onlySecondsValue))
{
return true;
}
//check if it is negative
if (_rawValue.StartsWith(__NEG))
{
_isNegative = true;
}
else _isNegative = false;
if (TimeSpan.TryParse(_rawValue, out _tsValue)) return true;
else return false;
}
/// <summary>
/// Determines time based on:
/// - if there is only one : in string means minutes and seconds
/// - if there are two : means hours and minutes and seconds
/// - in both cases milliseconds after . or , may be present
/// </summary>
/// <param name="sTime">String representing validated time</param>
/// <returns>Time span with translated time</returns>
private TimeSpan determineTime(string sTime)
{
int _iColon = Regex.Matches(sTime,__COL).Count;
string _sResult;
TimeSpan _tsDays = new TimeSpan();
if (TimeSpan.TryParse(sTime, out _tsDays))
{
if (_tsDays.Days > 0)
return new TimeSpan(0, _tsDays.Hours, _tsDays.Minutes, _tsDays.Seconds, _tsDays.Milliseconds);
}
TimeSpan _ts = new TimeSpan();
if (_iColon == 1) //minutes and seconds
{
//add 0 hours and 0 days
_sResult = addTimeToTimeSpan(sTime, "0.0:");
}
else if
(_iColon == 2) //hours minutes and seconds
{
//add 0 days
_sResult = addTimeToTimeSpan(sTime, "0.");
}
else _sResult = sTime;
if (TimeSpan.TryParse(_sResult, out _ts))
{
// in all cases remove day
return new TimeSpan(0, _ts.Hours, _ts.Minutes, _ts.Seconds, _ts.Milliseconds);
}
else return _ts;
}
/// <summary>
/// Returns time with added value, moves __NEG sign if present to the from
/// </summary>
/// <param name="sTime">Original time</param>
/// <param name="sTimeToAdd">Time to add</param>
/// <returns>string with complete time</returns>
private string addTimeToTimeSpan(string sTime, string sTimeToAdd)
{
string _sResult;
if (sTime.StartsWith(__NEG))
{
_sResult = __NEG + sTimeToAdd + sTime.Remove(0, 1);
}
else _sResult = sTimeToAdd + sTime;
return _sResult;
}
#endregion
#region Public events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region delegates
#endregion
#region Public delcarations
#region Dependency
public Brush BKColor
{
get { return (Brush)GetValue(BKColorProperty); }
set { SetValue(BKColorProperty, value); }
}
// Using a DependencyProperty as the backing store for BKColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BKColorProperty =
DependencyProperty.Register("BKColor", typeof(Brush), typeof(TimeTextBox), new PropertyMetadata(null));
public bool ReadOnly
{
get { return (bool)GetValue(ReadOnlyProperty); }
set {
SetValue(ReadOnlyProperty, value);
timeTextBox.ReadOnly = value;
}
}
// Using a DependencyProperty as the backing store for ReadOnly. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ReadOnlyProperty =
DependencyProperty.Register("ReadOnly", typeof(bool), typeof(TimeTextBox), new PropertyMetadata(null));
public string TimeText
{
get { return (string)GetValue(TimeTextProperty); }
set {
SetValue(TimeTextProperty, value);
//OnPropertyChanged("TimeText");
}
}
public static readonly DependencyProperty TimeTextProperty =
DependencyProperty.Register("TimeText", typeof(string), typeof(TimeTextBox),
new PropertyMetadata(string.Empty, OnTextPropertyChanged));
private static void OnTextPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
TimeTextBox myUserControl = dependencyObject as TimeTextBox;
myUserControl.OnPropertyChanged("TimeText");
myUserControl.OnTextPropertyChanged(e);
}
private void OnTextPropertyChanged(DependencyPropertyChangedEventArgs e)
{
txtTime.Text = TimeText;
}
public string Time
{
get { return txtTime.Text; }
set { txtTime.Text = value; }
}
#endregion
/// <summary>
/// True if time span is negative
/// </summary>
public bool IsNegative
{
get
{
return _isNegative;
}
}
/// <summary>
/// Gets or sets validated value background color
/// </summary>
public Brush ValueOKBackground
{
get
{
return _okBackground;
}
set
{
_okBackground = value;
}
}
/// <summary>
/// Gets or sets background color if value is not succesfully validated
/// </summary>
public Brush ValueNotOKBackground
{
get
{
return _okBackground;
}
set
{
_nokBackground = value;
}
}
#endregion
#region Public methods
#endregion
#region Constructors
public TimeTextBox()
{
InitializeComponent();
_okBackground = txtTime.Background;
}
#endregion
private void txtTime_TextChanged(object sender, TextChangedEventArgs e)
{
if (validate(txtTime.Text)) txtTime.Background = BKColor;
else
txtTime.Background = _nokBackground;
}
private void txtTime_LostFocus(object sender, RoutedEventArgs e)
{
//leave if nothing changed
if (txtTime.Text == _originalValue) return;
if (validate(txtTime.Text))
{
if (_bOnlySeconds)
{
int seconds = (int)Math.Truncate(_onlySecondsValue);
int milliseconds = (int)((_onlySecondsValue - seconds) * 1000);
_tsValue = new TimeSpan(0, 0, 0, seconds, milliseconds);
}
else
{
_tsValue = determineTime(txtTime.Text);
}
}
else
{
if (!validate(_originalValue)) //validate methods uses _tsValue to put validated timespan
{
_tsValue = new TimeSpan();
}
}
string _sign = _isNegative ? "-" : "";
txtTime.Text = _sign + _tsValue.ToString(__TFORMAT);
txtTime.Background = _isNegative ? _negBackground : BKColor;
TimeText = txtTime.Text;
txtTime.Background = BKColor;
OnPropertyChanged("UpdateTime");
}
private void txtTime_GotFocus(object sender, RoutedEventArgs e)
{
_originalValue = txtTime.Text;
if (!(validate(txtTime.Text))) txtTime.Text = "";
}
}
}
TimeText XAML
<UserControl x:Name="timeTextBox" x:Class="DataGridHratky.TimeTextBox"
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:DataGridHratky"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100">
<!-- DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TimeTextBox}}-->
<Grid x:Name="grdLayout">
<TextBox x:Name="txtTime" TextChanged="txtTime_TextChanged" LostFocus="txtTime_LostFocus" GotFocus="txtTime_GotFocus" BorderThickness="0"
Text="{Binding Path=TimeText, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TimeTextBox}}}"
IsReadOnly="{Binding Path=ReadOnly, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TimeTextBox}}}"
Background="{Binding Path=BKColor, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TimeTextBox}}}"
VerticalContentAlignment="Center"/>
</Grid>
</UserControl>
Main Window XAML
<Window x:Class="DataGridHratky.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:DataGridHratky"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgData" AutoGenerateColumns="False" AddingNewItem="dgData_AddingNewItem" BeginningEdit="dgData_BeginningEdit" CellEditEnding="dgData_CellEditEnding" RowHeight="25" AlternationCount="2" VerticalContentAlignment="Center" >
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="WhiteSmoke" />
</Trigger>
<DataTrigger Binding="{Binding Path=Selectable}" Value="False">
<DataTrigger.Setters>
<Setter Property="Background" Value="White" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="colCue" SelectedItemBinding="{Binding SelectedCue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="{Binding AvaiableCues}" Width="50" Header="Cue"/>
<DataGridTextColumn x:Name="colCaption" Width="200" Header="Popis" Binding="{Binding Caption}" />
<DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Čas efektu" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:TimeTextBox x:Name="ttbFireEffect" TimeText="{Binding Effect, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
BKColor="{Binding Path=Background, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn x:Name="colType" SelectedItemBinding="{Binding SelectedType, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="{Binding AvaiableFWTypes}" Header="Typ" Width="75" />
<DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Prodleni">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:TimeTextBox x:Name="ttbDelay" TimeText="{Binding Delay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ReadOnly = "{Binding IsDelayReadOnly}"
BKColor="{Binding Path=Background, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Čas odpalu" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:TimeTextBox x:Name="ttbFirePoint" TimeText="{Binding FiringPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
BKColor="{Binding Path=Background, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
FirepointManager Code
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataGridHratky
{
public class FirepointsManager : INotifyPropertyChanged
{
#region Enum
#endregion
#region constants
#endregion
#region private declarations
private List<string> _avaiableFirepoints = new List<string>();
private List<string> _avaiableFireworks = new List<string>();
#endregion
#region private methods
#endregion
#region Public events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Protected
protected void RaiseChange(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
#region delegates
#endregion
#region Public properties
/// <summary>
/// Firepoints Observable collection
/// </summary>
public ObservableCollection<FirePoint> FirePoints { get; set; }
/// <summary>
/// List of avaiable firepoints
/// </summary>
public List<string>AvaiableCues
{
get
{
return _avaiableFirepoints;
}
}
/// <summary>
/// List of pyrotechnics types
/// </summary>
public List<string>AvaiableFWTypes
{
get
{
return _avaiableFireworks;
}
}
#endregion
#region Public methods
/// <summary>
/// Adds a new Firepoint
/// </summary>
/// <param name="f">Firepoint</param>
public void AddFirePoint(FirePoint f)
{
f.SelectedCueChanged += F_SelectedCueChanged;
FirePoints.Add(f);
}
/// <summary>
/// Checks avaible cues and eliminates __MS if already used
/// </summary>
public void CheckSelectedCues()
{
foreach (var FirePoint in FirePoints)
{
if (FirePoint.SelectedCue == FirePoint.__MS)
{
if (_avaiableFirepoints.Contains(FirePoint.__MS))
_avaiableFirepoints.Remove(FirePoint.__MS);
return;
}
}
if (!_avaiableFirepoints.Contains(FirePoint.__MS))
_avaiableFirepoints.Add(FirePoint.__MS);
RaiseChange("CuesAvaiable");
}
private void F_SelectedCueChanged(object sender, EventArgs e)
{
CheckSelectedCues();
}
#endregion
#region Constructors
public FirepointsManager()
{
FirePoints = new ObservableCollection<FirePoint>();
FirePoints.CollectionChanged += FirePoints_CollectionChanged;
_avaiableFirepoints = FirePoint.GeneratedCues();
_avaiableFireworks = FirePoint.FireworksTypes();
}
private void FirePoints_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CheckSelectedCues();
}
#endregion
}
}

How to show TreeView on ComboBox.SelectedItem != null

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));
}

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();
}
}

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