げんさん日記

プログラミングで気付いた事等を書きます。

配列の要素を全て削除する。(メモリリーク対策)

2019年11月06日 10時13分18秒 | C#
配列要素を削除する場合にremoveとかclearを使用すると配列からは削除されるがメモリ上からは削除されない場合があります。その場合にメモリリークすることがあります。その対策としてDispose属性が存在する場合にはDisposeメソッドを実行し確実にメモリからの解放を行うものとする。その方法を下記に記します。

配列要素を全て削除する拡張クラスを作成します。
public static void RemoveAll<T>(this ICollection<T> values)
{
  // nullの時、処理を終了する。
  if (values == null)
  {
    return;
  }

  // 要素の数分繰り返します。
  foreach (var value in values)
  {
    // IDisposable型の時、Disposeを行います。
    IDisposable disp = value as IDisposable;
    if (disp != null)
    {
      disp.Dispose();
      disp = null;
    }
  }

  // 要素をクリアします。
  values.Clear();
}

使用例
public void OnClick()
{
  this.List.RemoveAll();
}
上記で配列情報がクリアされます。

Enumの内容をコンボボックス用のデータとして取得する。

2019年11月06日 09時49分12秒 | C#
enumに設定してある内容をコンボボックスのデータとして取得する方法を下記に記します。

enumの定義を下記のようにします。Descriptionに表示する内容を定義します。
public enum EncodeType
{
  [Description("shift_jis")]
  ShiftJis = 0,
  [Description("utf-8")]
  UTF8 = 1,
}

Enum値を取得する拡張クラスを作成します。
public static int Value(this Enum value)
{
  // Fieldを取得します。
  var fi = value.GetType().GetField(value.ToString());

  // Fieldから値を取得し返します。
  return (int)fi.GetValue(null);
}

Enum値に対応する名称を取得する拡張クラスを作成します。
public static string Name(this Enum value)
{
  // 指定されたEumのFieldを取得します。
  var fi = value.GetType().GetField(value.ToString());
  if (fi == null)
  {
    // 取得できなかった時、NULLを返します。
    return null;
  }

  // DescriptionAttributeを取得します。
  var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
  if (attributes != null && attributes.Length > 0)
  {
    // DescriptionAttributeが存在する時、DescriptionAttributeの内容を返します。
    return attributes[0].Description;
  }
  else
  {
    // DescriptionAttributeが存在しない時、値に対応する名称を返します。
    return value.ToString();
  }
}

コンボボックス用データを取得する拡張クラスを作成します。
public static ICollection GetComboData<T>(this T value)
{
  // Enumの値リストを取得します。
  var fields = value.GetType().GetFields();
  var enmName = value.GetType().Name;

  // 値リストからコンボデータを作成します。
  var datas = fields.ToList().Where(w => w.FieldType.Name == enmName).Select(s => new Models.ComboBoxModel()
   {
     Value = ((int)s.GetValue(null)).ToString(),
     Name = Name((Enum)s.GetValue(null)),
   }).ToList();

  // 作成したコンボデータを返します。
  return datas;
}
※Models.ComboBoxModelはValue とName を持つモデルです。

使用例
public void OnClick()
{
  var comboData = (default(EncodeType)).GetComboData();
}
上記でEnumの値を全て取得できますし、enumの内容が変更されても対応がenumの変更のみで終わる為、保守性が上がります。

enumの値を簡単に取得する。

2019年11月06日 09時16分31秒 | C#
enumを使う時にenumからintに変換することがあると思います。その辺りを簡単にできるようにしましたので下記に記します。

下記のenumがあります。
public enum EncodeType
{
  [Description("shift_jis")]
  ShiftJis = 0,
  [Description("utf-8")]
  UTF8 = 1,
}

Enum値を取得する拡張クラスを作成します。
public static int Value(this Enum value)
{
  // Fieldを取得します。
  var fi = value.GetType().GetField(value.ToString());

  // Fieldから値を取得し返します。
  return (int)fi.GetValue(null);
}

使用例
public void OnClick()
{
  int value = EncodeType.ShiftJis.Value();
}
わざわざintでキャストしなくても取れるようになりました。

指定された名前のコントロールにフォーカスを設定します。

2019年11月06日 08時41分48秒 | WPF
画面内にContentControl等が存在しても表示時のコントロールにフォーカスが当たるようにする方法を記します。
コントロールの一覧を取得する方法としてはLogicalTreeHelperとVisualTreeHelperで行う方法がありますが、LogicalTreeHelperではContentControlの内容まで検索されませんでしたのでVisualTreeHelperを使用しコントロールにフォーカスを設定します。

拡張プロパティを用意します。
public static void SetFocus(this DependencyObject parentControl, string setFocusControlName)
{
  // 子コントロールを全て取得し、コントロールの数分繰り返します。
  for (int i = 0; i
  {
    // 子コントロールを取得します。
    DependencyObject childObj = VisualTreeHelper.GetChild(parentControl, i);

    // 該当のコントロールかチェックします。
    if (childObj is FrameworkElement &&
      ((FrameworkElement)childObj).Name == setFocusControlName)
    {
      // 該当コントロールの場合はフォーカスを設定します。
      ((FrameworkElement)childObj).Focus();
      return;
    }

    // 対象コントロールに子コントロールが存在する場合は、再帰処理を行います。
    if (VisualTreeHelper.GetChildrenCount(childObj) > 0)
    {
      SetFocus(childObj, setFocusControlName);
    }
  }
}

使用例
public abstract class MainWindow : Window
{
  public void OnSetFocus(string controlName)
  {
    // コントロールにフォーカスを設定する。
    this.SetFocus(controlName);
  }
}
上記はコードビハインドでの使用例です。コントロール名はXamlのName属性です。

ViewModelのバインディングプロパティを簡単に定義する。

2019年09月12日 16時39分11秒 | WPF
ViewModelのバインディングプロパティを定義する場合にViewへの通知処理が存在する為、プロパティの定義がめんどくさくなります。
それを簡単に書く方法を記します。
また下記方法によりプロパティの解放を一括で行う為、メモリリークするコマンド等も解放されます。

BindingModel (画面通知するモデル)に下記内容を追加します。

プロパティを格納する箱を用意します。
public abstract class BindingModel : BaseModel
{
  // プロパティを管理する箱
  private Dictionary Propertys { get; set; } = new Dictionary();
}

次にGetプロパティを作成します。
protected T Get<T>([CallerMemberName] string name = null)
{
  // プロパティ名が無い場合はデフォルト値を返します。
  if (string.IsNullOrEmpty(name) == true)
  {
   return default(T);
  }

  // プロパティリストが無い場合はデフォルト値を返します。※Dispose時に発生する場合あり
  if (this.PropertieItems == null)
  {
   return default(T);
  }

  // プロパティリストから名前が同一の情報を取得し返します。
  object value = null;
  if (this.PropertieItems.TryGetValue(name, out value) == true)
  {
   return value == null ? default(T) : (T)value;
  }

  // プロパティリストに名前が同一の情報が無い時、デフォルト値を返します。
  return default(T);
}

次にSetプロパティを作成します。
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
  // プロパティ名が無い場合は処理を終了します。
  if (string.IsNullOrEmpty(name) == true)
  {
   return;
  }

  // プロパティリストが無い場合は処理を終了します。※Dispose時に発生する場合あり
  if (this.PropertieItems == null)
  {
   return;
  }
  // プロパティが存在するかチェックします。
  if (this.Propertys.ContainsKey(name) == false)
  {
    // キーが存在しない時、プロパティリストに追加します。
    this.Propertys.Add(name, value);

    // プロパティ変更通知を発生させます。
    this.NotifyPropertyChanged(name);
  }
  else
  {
    // キーが存在する時、値を変更します。
    if (Equals(value, this.Get(name)))
    {
      // 同一の値の場合は処理を終了します。
      return;
    }

    // プロパティの値を置き換えます。
    this.Propertys[name] = value;
    // プロパティ変更通知を発生させます。
    this.NotifyPropertyChanged(name);
  }
}

最後にまとめてプロパティを解放する処理を記述します。
protected override void Dispose(bool disposing)
{
  // 終了処理中の時は処理を行わない。
  if (base.Disposed == false)
  {
    // プロパティを全て解放する。
    this.Propertys?.RemoveAll();※
    this.Propertys = null;

    // ベースのDisposeを呼び出す。
    base.Dispose(disposing);
  }
}
※RemoveAllは拡張メソッドでIDisposable属性のものを探しDisposeを実行する処理を自作したものです。

使用例
public void TestViewModel : BindingModel
{
  // バインディングプロパティ
  public int Code
  {
    get { base.Get<int>(); }
    set { base.Set(value); }
  }
}
上記のようにバインディングプロパティがすっきりします。