Skip to content

Instantly share code, notes, and snippets.

@nexpr
Last active December 5, 2016 03:51
Show Gist options
  • Select an option

  • Save nexpr/7c1956bbed09de98b0426806c15be125 to your computer and use it in GitHub Desktop.

Select an option

Save nexpr/7c1956bbed09de98b0426806c15be125 to your computer and use it in GitHub Desktop.
BindingDataBase (WPF ViewModel Base)
using System;
namespace liblib.wpf
{
[AttributeUsage(AttributeTargets.Property)]
public class AutoValidateTarget : Attribute
{
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace liblib.wpf
{
public abstract class BindingDataBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
/// <summary>
/// エラー管理
/// </summary>
protected Map<string, HashSet<string>> errors { get; }
= new Map<string, HashSet<string>>(() => new HashSet<string>());
/// <summary>
/// プロパティの実体
/// </summary>
protected Dictionary<string, object> entities { get; }
= new Dictionary<string, object>();
/// <summary>
/// エラーを含むか
/// </summary>
public bool HasErrors => this.errors.Any(e => e.Value.Count > 0);
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 指定プロパティのエラー取得
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public IEnumerable GetErrors(string propertyName) => this.errors[propertyName];
/// <summary>
/// プロパティの変更を通知する
/// </summary>
/// <param name="property_name"></param>
protected void notifyPropertyChanged(string property_name)
=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property_name));
/// <summary>
/// エラーの変更を通知する
/// </summary>
/// <param name="property_name"></param>
protected void notifyErrorsChanged(string property_name)
=> this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(property_name));
/// <summary>
/// 指定プロパティ名の実体を返す
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="property_name"></param>
/// <param name="default_value"></param>
/// <returns></returns>
protected T getValue<T>(string property_name, T default_value = default(T))
{
if (!this.entities.ContainsKey(property_name))
{
this.entities[property_name] = default_value;
}
return (T)entities[property_name];
}
/// <summary>
/// 指定プロパティ名に値を保存
/// </summary>
/// <param name="property_name"></param>
/// <param name="value"></param>
protected void setValue(string property_name, object value)
{
object old_value;
if (this.entities.TryGetValue(property_name, out old_value) && Equals(old_value, value))
{
return;
}
object correct_value;
HashSet<string> errors = new HashSet<string>();
if (this.checkValidity(property_name, value, old_value, out correct_value, ref errors))
{
// valid
this.entities[property_name] = correct_value;
this.errors[property_name].Clear();
this.notifyPropertyChanged(property_name);
this.notifyErrorsChanged(property_name);
}
else
{
// invalid
this.entities[property_name] = correct_value;
this.errors[property_name] = errors;
this.notifyPropertyChanged(property_name);
this.notifyErrorsChanged(property_name);
}
}
/// <summary>
/// データのチェック
/// </summary>
/// <param name="property_name"></param>
/// <param name="value"></param>
/// <param name="corrected_value"></param>
/// <param name="errors"></param>
/// <returns></returns>
protected virtual bool checkValidity(string property_name, object value, object old_value, out object correct_value, ref HashSet<string> errors)
{
correct_value = value;
return true;
}
/// <summary>
/// AutoValidateAttribute 付きのプロパティをまとめてエラーチェック
/// </summary>
/// <returns></returns>
protected bool reportValidity()
{
var valid = true;
foreach (var property_info in this.GetType().GetProperties())
{
if (Attribute.GetCustomAttribute(property_info, typeof(AutoValidateTarget)) != null)
{
var value = property_info.GetValue(this);
object correct_value;
HashSet<string> errors = new HashSet<string>();
var name = property_info.Name;
if (this.checkValidity(name, value, value, out correct_value, ref errors))
{
// valid
this.entities[name] = correct_value;
this.errors[name].Clear();
this.notifyPropertyChanged(name);
this.notifyErrorsChanged(name);
}
else
{
// invalid
this.entities[name] = correct_value;
this.errors[name] = errors;
this.notifyPropertyChanged(name);
this.notifyErrorsChanged(name);
valid = false;
}
}
}
return valid;
}
/// <summary>
/// 規則に沿った名前のコマンドは自動でコマンド生成する
/// </summary>
protected void initCommands()
{
foreach (var property in this.GetType().GetProperties())
{
var lower_name = property.Name.ToLower();
if (lower_name.EndsWith("_command") && property.PropertyType == typeof(DelegateCommand))
{
var binding_flag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase;
var method = this.GetType().GetMethod(lower_name.Replace("_command", "_Execute"), binding_flag);
if (method == null)
{
continue;
}
var method_delegate = (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), this, method);
var check_method = this.GetType().GetMethod(lower_name.Replace("_command", "_CanExecute"), binding_flag);
if (check_method == null)
{
property.SetValue(this, new DelegateCommand(method_delegate));
}
else
{
var check_method_delegate = (Func<object, bool>)Delegate.CreateDelegate(typeof(Func<object, bool>), this, check_method);
property.SetValue(this, new DelegateCommand(method_delegate, check_method_delegate));
}
}
}
}
}
}
using System;
using System.Windows.Input;
namespace liblib.wpf
{
public class DelegateCommand : ICommand
{
private Action<object> execute { get; set; }
private Func<object, bool> canExecute { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute?.Invoke(parameter) ?? true;
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace liblib
{
public class Map<TKey, TValue> : Dictionary<TKey, TValue>
{
Func<TValue> default_value_generator { get; }
bool create_value_if_not_exist { get; }
public Map(bool create_value_if_not_exist = true)
{
this.default_value_generator = () => default(TValue);
this.create_value_if_not_exist = create_value_if_not_exist;
}
public Map(Func<TValue> default_value_generator, bool set_with_get = true)
{
this.default_value_generator = default_value_generator;
this.create_value_if_not_exist = create_value_if_not_exist;
}
public new TValue this[TKey key]
{
get
{
if (key == null || !this.ContainsKey(key))
{
var value = this.default_value_generator();
if (key != null && this.create_value_if_not_exist)
{
base[key] = value;
}
return value;
}
else
{
return base[key];
}
}
set
{
base[key] = value;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment