2014年11月14日金曜日

寸法×個数を、MultiBindingとIMultiValueConverterで

幅×個数 という感じのデータが有りまして、丁度、MultiBindingが活躍しました。

2通りの表現を、2つのフィールドで。

(1) 幅
(2) 幅x個数

幅を入力したい。しかし、分割する場合も有る。では、最初は3つだけ分割→幅1,幅2,幅3。いやいや、そうではなく、何個かに分ける場合も有る→幅1x幅1個数,幅2x幅2個数,幅3x幅3個数。これでも表現できないものは備考に、という流れになりました。

MultiBindingの性質として、「入力は1つ以上」「出力は1つだけ」という条件が有ります。丁度これに合っています。

XAML:

<GridViewColumn Header="W1" Width="70" local:LvSortSpec.Columns="幅1" local:GvcManager.Key="幅1" >
 <GridViewColumn.CellTemplate>
  <DataTemplate>
   <TextBlock HorizontalAlignment="Center">
    <TextBlock.Text>
     <MultiBinding Converter="{StaticResource 寸法個数Converter}">
      <Binding Path="幅1" />
      <Binding Path="幅1個数" />
     </MultiBinding>
    </TextBlock.Text>
   </TextBlock>
  </DataTemplate>
 </GridViewColumn.CellTemplate>
</GridViewColumn>

お分かりとは思いますが、 "寸法個数Converter"を、リソース辞書で定義するようにしてください。

<local:寸法個数Converter x:Key="寸法個数Converter" />

C#

 public class 寸法個数Converter : IMultiValueConverter {
  public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
   if (true
    && values != null
    && values.Length == 2
   ) {
    if (!object.ReferenceEquals(values[0], DependencyProperty.UnsetValue)) {
     if (!object.ReferenceEquals(values[1], DependencyProperty.UnsetValue)) {
      return values[0] + "×" + values[1];
     }
     return "" + values[0];
    }
   }
   return "";
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
   throw new NotImplementedException();
  }
 }

DependencyProperty.UnsetValue が出現します。値が無い場合、nullか、DBNull.Valueが来るのかと思いきや、違って、こういう値がくるようです。

2014年11月13日木曜日

BindingしているTextBoxで、閉じた時に更新されない

次の2行のコードは恐らく等価でありますので、フォーカスを失うまで、Bindingは書き戻しされません。

<TextBox Text="{Binding 品番}" />
<TextBox Text="{Binding 品番,UpdateSourceTrigger=LostFocus}" />

特にWindowを閉じた場合、書き戻しされない事が有り、結構困ったりします。

次のように書けば解決なのですが、全てに UpdateSourceTrigger=PropertyChanged を追加していくのも気が遠くなる話です。

<TextBox Text="{Binding 品番,UpdateSourceTrigger=PropertyChanged}" />

そこで、BindingGroupを使って、楽をしています。こちらに詳細を残しています。

*但し、この方法には副作用が有ります。
DataRowViewへアクセスする前に、BindingGroup.CommitEdit(); を呼ばないと、DataRowViewに反映されません。
メソッドの入り口にCommitEditが多くなるきらいが有りますが… そこはバランスを考えます。

WPFで、表示しないWindowを作るとメモリリーク発生か

WPFの設計思想をよくしらないのでアレなのですが、、、

作成したWindowをCloseしないとメモリリークの原因になり、アプリが終了しません。。。

namespace WpfApplication17 {
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application {
        protected override void OnStartup(StartupEventArgs e) {
            Window w1 = new Window();
            w1.Close();
            Window w2 = new Window();
            w2.Close();
        }
    }
}

上の例はわかりやすいと思います。何も表示しないで終了するWPFアプリです。きちんと終了してくれます。

namespace WpfApplication17 {
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application {
        protected override void OnStartup(StartupEventArgs e) {
            Window w1 = new Window();
            w1.Close();
            Window w2 = new Window();
            //w2.Close();  <-- 閉じないようにする
        }
    }
}

上の例はw2.Close()をコメントにしました。終了しません。

とにかく、作ったWindow等は
  • 表示したら、閉じる。
  • 表示しない場合は、意図的にClose()を呼ぶ。
これらをしない事には、アプリが終了しないという事でした。。。