Creating custom GroupDescription based on DateTime - wpf

I'm grouping some data and PropertyGroupDescription works fine most of the time. However if that property is a DateTime and i wan't to group several dates together as one group (like 30 days in each group or something) I would need a new GroupDescription. Problem is I have no idea how the class actually works and how I would design such a class.
I'm hoping to be able to inherit PropertyGroupDescription (instead of the basic abstract class) because this will also be based on a property but here I'm grouping based on a range of values instead of a single value == 1 group.
Any guide or even a ready class like this?

A bit late, but as you say yourself IValueConverter can be used for this - here's a simple converter I used once that will group by a friendly relative date string:
public class RelativeDateValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var v = value as DateTime?;
if(v == null) {
return value;
}
return Convert(v.Value);
}
public static string Convert(DateTime v)
{
var d = v.Date;
var today = DateTime.Today;
var diff = today - d;
if(diff.Days == 0) {
return "Today";
}
if(diff.Days == 1) {
return "Yesterday";
}
if(diff.Days < 7) {
return d.DayOfWeek.ToString();
}
if(diff.Days < 14) {
return "Last week";
}
if(d.Year == today.Year && d.Month == today.Month) {
return "This month";
}
var lastMonth = today.AddMonths(-1);
if(d.Year == lastMonth.Year && d.Month == lastMonth.Month) {
return "Last month";
}
if(d.Year == today.Year) {
return "This year";
}
return d.Year.ToString(culture);
}
public static int Compare(DateTime a, DateTime b)
{
return Convert(a) == Convert(b) ? 0 : a.CompareTo(b);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can then use it like this:
view.GroupDescriptions.Add(
new PropertyGroupDescription("Property",
new RelativeDateValueConverter()));

Related

WPF DatePicker - write custom datetime format

i have in my application DatePicker. I would like write standart DateTime format (e.g. 2016-10-18), select DateTime from calendar or write custom format (e.g. ddmmrrrr, ddmm, ddmmrr...)
I made DateTimeConverter:
class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime emptyDateTime;
if (DateTime.TryParse(value.ToString(), out emptyDateTime) && emptyDateTime == DateTime.MinValue)
return DateTime.Now;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int valueNumber;
string valueStr = value.ToString().Replace(".", "");
if (!int.TryParse(valueStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out valueNumber))
return DateTime.Now;
if (valueStr.Length == 8) //format ddmmrrrr
{
DateTime emptyDateTime;
//for removing empty datetime (01.01.0001)
return DateTime.TryParse(value.ToString(), out emptyDateTime) && emptyDateTime == DateTime.MinValue
? (object) null
: new DateTime(valueNumber%10000, (valueNumber/10000)%100, valueNumber/1000000);
}
if (valueStr.Length == 6) //format ddmmrr
return new DateTime(2000 + valueNumber % 100, (valueNumber / 100) % 100, valueNumber / 10000);
if (valueStr.Length == 4) //format ddmm
return new DateTime(DateTime.Now.Year, valueNumber % 100, valueNumber / 100);
return DateTime.Now;
}
}
And this is my DatePicker:
<DatePicker Focusable="True" Grid.Column="4"
Text="{Binding Order.DateTime, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}"
PreviewKeyDown="DatePicker_OnPreviewKeyDown"/>
This is property for binding:
class Order{
public DateTime DateTime { get; set; }
}
My problems are:
When DatePicker is null, value in Order.DateTime is 01.01.0001
When I write my format (ddmmrrrr), value in Order.DateTime is DateTime.Now
I dont see whats wrong. Thanks
your Converter checks the TryParse wrong. Change it to:
if (!DateTime.TryParse(value.ToString(), out emptyDateTime) || emptyDateTime == DateTime.MinValue)
return DateTime.Now;
return value;

How can I override the special filter operands "Blanks" and "NonBlanks" for XamDataGrid?

first: we use infragistics xamdatagrid 11.1.20111.2053
our problem:
We use the grid with generic lists. So it's very dynamic and must be prepared for any situation. We set for each field type theSortComparer, FilterComparer, the editor type, Edita type and style editor.
For some properties of a model, we use special TypeConverter.
For example, in a cell, some values ??can not be displayed.
0 = string.Empty
1 = 1
2 = 2
first solution, we only use the type converter and a special sort comparer:
public class HideZeroIntEntryConverter : Int32Converter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (value is int) {
if (destinationType == typeof(string)) {
return ((int)value != 0) ? value.ToString() : string.Empty;
}
return ((int)value != 0) ? value : Binding.DoNothing; // this is the best solution to tell the grid the cell is empty
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
this works perfect if we not decide to filter, but if we want to filter the values we see the ugly "Binding.DoNothing" in the filter drop down items (the sorting and filtering is also wrong).
also, we can not filter for "0" because we the converter says string.empty...
second solution, we use a special XamTextEditor:
public class HideZeroIntEntryTextEditor : XamTextEditor
{
public HideZeroIntEntryTextEditor() {
this.ValueToDisplayTextConverter = new HideZeroIntEntryValueConverter();
}
}
public class HideZeroIntEntryValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int) {
if (targetType == typeof(string)) {
return ((int)value != 0) ? value.ToString() : string.Empty;
}
// this never happens
return ((int)value != 0) ? value : Binding.DoNothing;
}
// this never happens
return targetType == typeof(string) && value != null ? value.ToString() : value;
}
}
and for the field settings
field.Settings.EditAsType = typeof(int);
field.Converter = null;
field.Settings.EditorType = typeof(HideZeroIntEntryTextEditor);
field.Settings.SortComparer = GenericComparer<int>.Instance;
field.Settings.FilterComparer = GenericComparer<int>.Instance;
field.Settings.GroupByComparer = GroupByRecordComparer<int>.Instance;
Now we can filter to "0", even if this does not appear in the list.
But, in both cases, we can not filter by empty entries, because it actually does not exist!
We want to though!
In our opinion, this could be if we could make our own special filter. But this is unfortunately not so easy.
Yes, we can remove the special filter blanks and NonBlanks, BUT that applies to all grids.
A special filter to override is very complicated and does not even correct.
Ok, we want see empty cell, and want filter this cells, but the special filter doesn't works correct!
What can we do, any ideas?
here is the question at infragistics
I answered this on the Infragistics forums right here with a sample.
You can use the RecordFilterDropDownPopulating event to remove the existing FilterDropDownItem for "(Blanks)" and replace it with a custom one for the value of 0 when the editor for the field is HideZeroIntEntryTextEditor. The following code accomplishes this:
void XamDataGrid1_RecordFilterDropDownPopulating(
object sender,
//Infragistics.Windows.DataPresenter.Events.
RecordFilterDropDownPopulatingEventArgs e)
{
if (e.Field.EditorTypeResolved == typeof(HideZeroIntEntryTextEditor)) {
bool found = false;
int index = 0;
while (index < e.DropDownItems.Count && !found) {
FilterDropDownItem item = e.DropDownItems[index];
if (item.DisplayText == "(Blanks)") {
e.DropDownItems.Remove(item);
e.DropDownItems.Insert(index, new FilterDropDownItem(
new ComparisonCondition(ComparisonOperator.Equals, 0), "(Blanks)"));
found = true;
}
index++;
}
}
}

When there are no domain models, is the ViewModel for the Model used to keep the DAL out of the ViewModel of the View?

I am currently working on an application that has all its entities coming from straight from Linq2SQL. Trying to find a way to keep everything cleanly separated, I added a domain model for the type that came from Linq2SQL and wrapped that with a ViewModel.
This added a little bit of complexity to my code, as I have a service layer and in it when initializing I got the entity from L2S, newed my domain class, and filled it with whatever data was in the entity.
When I wanted to insert the item back into the database in L2S, I ran into a problem which made me do the reverse: fill an entity with the data from the domain class. At this point I started to doubt if I was on the right path, so I started to think about what I have done wrong, or just possibly thought was the right way but ultimately wasn't.
I ended up thinking that wrapping the entity instead of filling a domain model and wrapping that, was maybe the right way to go in this situation. If I wouldn't do something like this, I would need to have a using statement in the ViewModel of my View pointing to the DAL. Maybe I'm mistaken, but as far as I read (in books, internet articles), that is not a clean separation of concerns.
So, hence my question:
When there are no domain models, is the ViewModel for the Model used to keep the DAL out of the ViewModel of the View?
Your question is a bit vague. However I see a lot of confusion in the comunity about the MVVM pattern. A lot of people "Wrap" the model in the ViewModel, like what you're doing, and this is wrong.
The Model is basically your L2S Objects.
The ViewModel, exposes the L2S objects directly, and handles interaction logic, i.e. handle commands, object updates, etc. (doesn't wrap anything, just as a link to them).
Wraping is wrong, let's say for example that you want something that is too complex for a converter, yet you need to have it on your Entity (BusinessObject or whatever comes out of L2S - Your ORM framework), you should extend your Entity to support this.
I'll give you some examples, however these are from a slightly different architecture:
Entity Framework 4.1
Prism, MVVM implementation
MEF Dependency Injection Container
This is a list of Tasks in this application, the final result is a view like project with a gantt chart.
The ViewModel looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using Sigep.Common.Interfaces;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.ViewModel;
using System.Windows.Input;
using System.Diagnostics;
using Sigep.Common.DataSelfTracking;
using System.Windows.Data;
using System.Globalization;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Media;
using System.Threading;
using System.Threading.Tasks;
namespace Sigep.WPF.Controls.Cronograma
{
[Export(typeof(TarefasListViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class TarefasListViewModel : NotificationObject
{
private IDataManager _data;
private IRegionManager _regionManager;
private static ObservableCollection<Tarefa> _tarefas;
private static List<Sigep.Common.DataSelfTracking.Tarefa> _tarefasList;
[ImportingConstructor]
public TarefasListViewModel(IRegionManager regionManager, IDataManager data)
{
_data = data;
_regionManager = regionManager;
_tarefas = new ObservableCollection<Tarefa>(_data.TarefaList.OrderBy(T => T.Codigo));
_tarefasList = _data.TarefaList;
_data.Loaded += new EventHandler<DataManagerEventArgs>(Data_Loaded);
_NovaTarefa_Command = new DelegateCommand(this.NovaTarefa);
}
void Data_Loaded(object sender, DataManagerEventArgs e)
{
Task.Factory.StartNew(() =>
{
_tarefas.Clear();
foreach (var tarefa in _data.TarefaList.OrderBy(T => T.Codigo)) _tarefas.Add(tarefa);
RaisePropertyChanged(() => this.Promotores);
}
, CancellationToken.None
, TaskCreationOptions.None
, Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<TaskScheduler>());
}
public ObservableCollection<Tarefa> Tarefas
{
get
{
return _tarefas;
}
}
public static void refreshTarefas()
{
_tarefas.Clear();
foreach (var tarefa in _tarefasList.OrderBy(T => T.Codigo)) _tarefas.Add(tarefa);
}
public IEnumerable<PromotorIndex> Promotores
{
get
{
int index = 0;
foreach (var promotor in _data.Candidatura.Promotores.OrderBy(p => p.Nome))
{
yield return new PromotorIndex()
{
Nome = promotor.Nome,
Index = index++
};
}
}
}
private ICommand _NovaTarefa_Command;
public ICommand NovaTarefa_Command { get { return this._NovaTarefa_Command; } }
private void NovaTarefa()
{
Tarefa tarefa = new Tarefa { Inicio = DateTime.Now, Fim = DateTime.Now, Nome = "",Actividade = _data.ActividadeList.FirstOrDefault() };
IRegionManager _region = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IRegionManager>();
foreach (var v in _region.Regions["CrudCronogramaTarefas"].Views) _region.Regions["CrudCronogramaTarefas"].Remove(v);
_region.Regions["CrudCronogramaTarefas"].RequestNavigate("/TarefaDefault", nr => { });
var view = ((FrameworkElement)_region.Regions["CrudCronogramaTarefas"].ActiveViews.FirstOrDefault());
((UserControlCrudBase)view).Permissions = Sigep.WPF.Controls.UserControlCrudBase.PermissionsType.Update;
view.DataContext = tarefa;
}
}
public class PromotorIndex
{
public string Nome { get; set; }
public int Index { get; set; }
}
// Converters
public class DataInicioPercentagemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tarefa = (Tarefa)value;
var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;
double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
double diasTarefaInicio = tarefa.Inicio.Subtract(candidatura.DataInicio).Days;
return new GridLength((diasTarefaInicio / totalDias * 100), GridUnitType.Star);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public class DataMeioPercentagemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tarefa = (Tarefa)value;
var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;
double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
double diasTarefa = tarefa.Fim.Subtract(tarefa.Inicio).Days;
return new GridLength((diasTarefa / totalDias * 100), GridUnitType.Star);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public class DataFimPercentagemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tarefa = (Tarefa)value;
var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;
double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
double diasTarefaFim = candidatura.DataFim.Subtract(tarefa.Fim).Days;
return new GridLength((diasTarefaFim / totalDias * 100), GridUnitType.Star);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
[ValueConversion(typeof(int), typeof(Brush))]
public class IndexColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(Brush))
throw new InvalidOperationException("The target must be a Brush");
switch((int)value)
{
case 0:
return Brushes.Red;
case 1:
return Brushes.Green;
case 2:
return Brushes.Blue;
case 3:
return Brushes.Purple;
case 4:
return Brushes.Yellow;
case 5:
return Brushes.Brown;
default:
return Brushes.Pink;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(int))
throw new InvalidOperationException("The target must be a int");
var color = (Brush)value;
if (color == Brushes.Red) return 0;
else if (color == Brushes.Green) return 1;
else if (color == Brushes.Blue) return 2;
else if (color == Brushes.Purple) return 3;
else if (color == Brushes.Yellow) return 4;
else if (color == Brushes.Brown) return 5;
else return -1;
}
}
}
As you can see it has specific converters there, some objects we use to create some extra UX functionality and handles command interaction directly from the View.
The Task (here called Tarefa from portuguese naming) itself is an Entity Framework Self Tracking Entity, and it is extended by adding a second partial class definition. The STE T4 template itself isn't that much tweaked, most of the customization is done by adding extra partial class definition to objects we want to customize.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sigep.Common.DataSelfTracking.Utils;
namespace Sigep.Common.DataSelfTracking
{
[System.Diagnostics.DebuggerDisplay("{Codigo}")]
public partial class Tarefa
{
private const double GRID_HEIGHT = 32;
public Actividade ActividadeNum
{
get
{
return Actividade;
}
set
{
Actividade = value;
this.Numero = this.Actividade.Tarefas.Max(X=> X.Numero)+1;
}
}
public Candidatura CandidaturaActual
{
get
{
if (RelsRecursosInternosTarefas.Count > 0)
return this.RelsRecursosInternosTarefas[0].RecursoInterno.Estabelecimento.Promotor.Candidatura;
else if (RelsRecursosExternosTarefas.Count > 0)
return this.RelsRecursosExternosTarefas[0].RecursoExterno.EntidadeExterna.Promotores[0].Candidatura;
else
return null;
}
}
public double TotalHoras
{
get
{
return RelsRecursosInternosTarefas.Sum(r => r.Duracao) + RelsRecursosExternosTarefas.Sum(r => r.Duracao);
}
}
public string Codigo
{
get
{
return Actividade.PKID.ToString() + "." + Numero;
}
}
public int DuracaoDias
{
get
{
return (int)Math.Ceiling(DateTimeUtils.CalculateBusinessDays(Inicio, Fim));
}
}
public IEnumerable<AlocacaoPromotorTarefa> PercentagensParticipacao
{
get
{
var altura = GRID_HEIGHT / CandidaturaActual.Promotores.Count;
int index = 0;
foreach (var promotor in CandidaturaActual.Promotores.OrderBy(p => p.Nome))
{
var totalRI = RelsRecursosInternosTarefas.Where(r => r.RecursoInterno.Estabelecimento.Promotor == promotor).Sum(r => r.Duracao);
var totalRE = RelsRecursosExternosTarefas.Where(r => r.RecursoExterno.Estabelecimento.Promotor == promotor).Sum(r => r.Duracao);
yield return new AlocacaoPromotorTarefa() {
Actual = totalRI + totalRE,
Restante = TotalHoras - totalRI - totalRE,
Index = index++,
GridHeight = altura
};
}
}
}
public DateTime GetPrimeiroDiaAno(int ano)
{
if (ano < Inicio.Year || ano > Fim.Year) throw new Exception("Ano Invalido");
else if (Inicio.Year == ano) return Inicio;
else return new DateTime(ano, 1, 1);
}
public DateTime GetUltimoDiaAno(int ano)
{
if (ano < Inicio.Year || ano > Fim.Year) throw new Exception("Ano Invalido");
else if (Fim.Year == ano) return Fim;
else return new DateTime(ano, 12, 31);
}
public int GetDuracaoDias(int ano)
{
if (ano < Inicio.Year || ano > Fim.Year) return 0;
else if (ano == Inicio.Year && ano == Fim.Year) return (int)DateTimeUtils.CalculateBusinessDays(Inicio, Fim) + 1;
else if (ano == Inicio.Year) return (int)DateTimeUtils.CalculateBusinessDays(Inicio, new DateTime(ano, 12, 31)) + 1;
else if (ano == Fim.Year) return (int)DateTimeUtils.CalculateBusinessDays(new DateTime(ano, 1, 1), Fim) + 1;
else return (int)DateTimeUtils.CalculateBusinessDays(new DateTime(ano, 1, 1), new DateTime(ano, 12, 31));
}
public double GetDuracaoMeses(int ano)
{
if (ano < Inicio.Year || ano > Fim.Year) return 0;
else if (ano == Inicio.Year && ano == Fim.Year) return DateTimeUtils.CalculateMonths(Inicio, Fim);
else if (ano == Inicio.Year) return DateTimeUtils.CalculateMonths(Inicio, new DateTime(ano, 12, 31));
else if (ano == Fim.Year) return DateTimeUtils.CalculateMonths(new DateTime(ano, 1, 1), Fim);
else return 12;
}
}
public class AlocacaoPromotorTarefa
{
public double Actual { get; set; }
public double Restante { get; set; }
public int Index { get; set; }
public double GridHeight { get; set; }
}
}
As you can see, it's mostly getters that return stuff that we want to bind to the View directly, that isn't apart of the Model. This practice makes development very fast, as writing converters can be quite complex for some types of exposures.
This is a lot of code, but hopefully will give you an idea of what to write on the ViewModel and what to write on the Model, how to extend the Model and how you bind stuff around.

INotifyPropertyChanged does not update Value Converter

I have some properties which implement the INotifyPropertyChanged interface. It works fine. But in my code I also use some value converters (if value < 3 - make grid red, if value >3 and value < 10 - make grid blue, etc.).
The problem is how to refresh value converter after PropertyChanged was raised?
Is there simple code behind solution?
Thanks all and sorry for my bad English!
Here some code:
public class NotifyColors : INotifyPropertyChanged
{
private Color _TodayColor;
public Color TodayColor
{
get
{
return _TodayColor;
}
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
// it raised correctly when I change color with color picker control
}
}
}
// here is value converter
[ValueConversion(typeof(object), typeof(Brush))]
public class PositionToBackgroundConverter : IValueConverter
{
ModulePreferences ModulePrefs;
public PositionToBackgroundConverter(ModulePreferences ModulePrefs)
{
this.ModulePrefs = ModulePrefs;
}
#region IValueConverter Member
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (ModulePrefs.UseColoringByPosition)
{
try
{
if (value != null)
{
short value_short = (short)value;
if (value_short <= 3)
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj._TodayColor); // here is changing property
else
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj.T100PlusColor);
}
else
return Brushes.Transparent;
}
catch
{
return Brushes.Transparent;
}
}
else
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
And here I apply my value converter to the grid:
// assign backgroundconverter
var grid = new FrameworkElementFactory(typeof(Grid));
bin = new Binding();
bin.Path = new PropertyPath(string.Format("DataItem.{0}", LastPositionColumnName));
bin.Converter = new PositionToBackgroundConverter(ProjectViewObj.ModulePrefs);
grid.SetValue(Grid.BackgroundProperty, bin);
If you have done it "correctly" the PropertyChanged event will cause an update of the bindings which bind to that property, when this occurs any converters that are used in the same binding will reconvert the values. So normally the conversions happen on their own. If this is not the case you are probably using the converters "inappropriately", please post some code because without it it's quite impossible to tell what exactly you are doing wrong.
Edit: You use grid.SetValue(Grid.BackgroundProperty, bin), you should use grid.SetBinding(Grid.BackgroundProperty, bin) instead since it is a binding.
Edit2: This really has nothing to do with converters.
In your sample code you bind to IntValue, then you change TodayColor and expect the binding to be updated, not gonna happen. If you want the binding to react to both properties you have to either use a MultiBinding or raise the respective events since your properties are interdependent.
i.e.
private Color _TodayColor;
public short _IntValue;
public short IntValue
{
get { return _IntValue; }
set
{
if (_IntValue != value)
{
_IntValue = value;
OnPropertyChanged("IntValue");
OnPropertyChanged("TodayColor");
}
}
}
public Color TodayColor
{
get { return _TodayColor; }
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
OnPropertyChanged("IntValue");
}
}
}

WPF PropertyGrid Problem

I am trying to create a WPF based PropertyGrid.
Recently i tried wpg.codeplex.com project, but i had some problems with this control.
Now, i am trying to develop http://blog.joachim.at/?p=36 this project.
I successfully added Enum Values, support but i have got problems with collections.
For example my custom object has a property that name is City and type is Guid.
I want, users can select City from combobox.
I was fighting with TypeConverts, IValueConverts, and etc..
How can i solve this?
After hours of work i solved my problem.
I had need only TypeConverter to solve this, so i created a class that derives from TypeConverter.
class PierListConverter : TypeConverter
{
ArrayList piers = new ArrayList();
public PierListConverter()
{
}
public override bool
GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
// This method returns me the list that will use to fill combo at property grid.
piers.Clear();
foreach (var item in GullsEyeModel.GetInstance().GetPiers())
{
piers.Add(item.Id);
}
StandardValuesCollection cols = new StandardValuesCollection(piers);
return cols;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
// If this method returns true, ConvertFrom method will invoke
if (sourceType == typeof(string))
{
return true;
}
else
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
// In this method i am getting selected text and querying; after that i retrieve proparete Guid value and then returning back to my object that binded property grid.
if (value != null)
{
if (value.ToString() == "Seçiniz")
{
return Guid.Empty;
}
else if (!string.IsNullOrEmpty(value.ToString()))
{
GuidConverter g = new GuidConverter();
PierItem[] pierArray = GullsEyeModel.GetInstance().GetPiers();
PierItem selectedPier = pierArray.Where(item => item.Info.Name == value.ToString()).FirstOrDefault();
if (selectedPier != null)
{
return selectedPier.Id;
}
else
return base.ConvertFrom(context, culture, value);
}
else
return base.ConvertFrom(context, culture, value);
}
else
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
// In this method i am converting ID to string (Name) to display in Property Grid
if (value != null)
{
GuidConverter g = new GuidConverter();
PierItem[] piers = GullsEyeModel.GetInstance().GetPiers();
PierItem selectedPier = piers.Where(item => item.Id== (Guid)g.ConvertFromString(value.ToString())).FirstOrDefault();
if (selectedPier != null)
{
return selectedPier.Info.Name;
}
else
return "Seçiniz";
}
else
return base.ConvertTo(context, culture, value, destinationType);
}
}
Using custom TypeConverter
class MyClass
{
// my some props..
// my some props..
[TypeConverter(typeof(PierListConverter))]
public Guid PierId {get; set;}
// my some methods..
}

Resources