2017-07-18 5 views
2

私は現在、ユーザが3つの別々のスライダを介して3つの値を選択できるユーザインタフェースを開発中です。 .xamlで3つのスライダ値を1つのTexblock Text/Label Contentに結合しますか?

:.xaml.csで

<Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold" HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/> 
<Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontStyle="Italic"/> 
<Slider x:Name="SampleAmountSlider" HorizontalAlignment="Left" Margin="181,14,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64" ValueChanged="SampleAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/> 
<TextBlock x:Name="SampleSliderValue" HorizontalAlignment="Left" Margin="165,16,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/> 
<Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top" FontStyle="Italic"/> 
<Slider x:Name="CalAmountSlider" HorizontalAlignment="Left" Margin="181,40,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7" ValueChanged="CalAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/> 
<TextBlock x:Name="CalSliderValue" HorizontalAlignment="Left" Margin="165,42,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/> 
<Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top" FontStyle="Italic"/> 
<Slider x:Name="ControlAmountSlider" HorizontalAlignment="Left" Margin="181,66,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4" ValueChanged="ControlAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/> 
<TextBlock x:Name="ControlSliderValue" HorizontalAlignment="Left" Margin="165,68,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/> 
<Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left" Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/> 
<TextBlock x:Name="TotalPrepValue" HorizontalAlignment="Left" Margin="225,110,0,0" FontWeight="Bold" FontStyle="Italic" Text="0" VerticalAlignment="Top"/> 

private void SampleAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 
    { 
     SampleSliderValue.Text = Math.Round(e.NewValue, 0).ToString(); 
    } 

private void CalAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 
    { 
     CalSliderValue.Text = Math.Round(e.NewValue, 0).ToString(); 
    } 

private void ControlAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 
    { 
     ControlSliderValue.Text = Math.Round(e.NewValue, 0).ToString(); 
    } 

私は、特定のスライダの現在の値を示すために、スライダーの前で3つのテキストブロックを持っています私が望むのは、最後のテキストブロック(TotalPrepValueという名前の)は、患者サンプルの量、較正者の量、および対照サンプルの量の合計を含むことです。

私はたくさんの質問をしているのか、それとも不明な点があるのか​​分かりません(もしそうなら、教えてください。できるだけ早く回答します)。 私は非常に経験の浅いプログラマーであり、学びたいと思っています! ご協力いただきありがとうございます!

+1

レイアウトを作成する代わりに、 'Margin'のコンテナ(行/列の' Grid'、 'StackPanel'、等)を使用してみてください。問題は、すべてのイベントハンドラが呼び出すメソッドに計算を移し、そこでテキストを設定します。バインディング+ MVVMの使用を開始し、プロパティ 'public double Sum => ...'を作成し、sumを計算するために使用されるすべてのプロパティの通知( 'INotifyPropertyChanged')を上げる方が簡単です。 – Sinatr

+0

@Sinatrご回答いただきありがとうございます!できるだけ早くこれを調べて、成功すれば教えてあげましょう! – WSU

+0

binding + mvvmもっと簡単にアプローチできますか?あなたは深刻ですか?始まっている男のためにそれはwaaayyより難しいです。それは彼が望むことをする "プロ"の方法かもしれませんが、確かに最も簡単ではない、彼はスライダが変わるたびにテキストを更新するメソッドを作成することができます。 –

答えて

1

このシナリオでは、お勧めのようにIMultiValueConverterを使用できます。しかし、他のシナリオでは有用なツールですが、それはここの仕事にとって間違ったツールです。その理由は、この場合、非UIデータを格納する場所としてのUI要素の不適切な使用を永続させるためです。

WPFプログラムの作成中は、MVVMスタイルのプログラミングに従うことをお勧めします。用語「MVVM」は、文字通り「モデル、ビュー、ビューモデル」を意味します。その観点から、モデルとビューの間には、常に専用の「アダプター」タイプがあります。しかし、MVVMのパラダイムの重要な部分は、ビューロジックをモデルロジックから分離することについて厳密にすることです。これは、「ビューモデル」タイプの余分なレイヤーなしで行うことができます。これにより、MVVMはMVC(「モデル、ビュー、コントローラ」)およびMVP(「モデル、ビュー、プレゼンター」)と同じツールセットに置かれます。

これらのすべての鍵は、モデルデータ構造で表現されるビジネスロジックを持ち、値の変更された通知(WPFでは、主なメカニズムはINotifyPropertyChanged)を提供する型によって実装されていることです。完全に別々に表現されたロジックを表示することもできます(WPFでは、ほとんどの場合、多くの場合、XAMLで宣言されています)。

この例では、サンプルデータ、キャリブレータ、コントロール、および合計プリペット数など、関心のあるデータを表すモデルデータ構造が必要です。最後のものは単に最初の3つの合計です。これらを追跡し、他の3つが変更されたときに合計値を正しく更新できるクラスがあれば、XAMLで宣言されたビューにこれを直接バインドすることができます。すべて。例えば

class ViewModel : INotifyPropertyChanged 
{ 
    private int _sampleCount; 
    public int SampleCount 
    { 
     get { return _sampleCount; } 
     set { _UpdateField(ref _sampleCount, value, OnCountChanged); } 
    } 

    private int _calibratorCount; 
    public int CalibratorCount 
    { 
     get { return _calibratorCount; } 
     set { _UpdateField(ref _calibratorCount, value, OnCountChanged); } 
    } 

    private int _controlCount; 
    public int ControlCount 
    { 
     get { return _controlCount; } 
     set { _UpdateField(ref _controlCount, value, OnCountChanged); } 
    } 

    private int _totalPrepCount; 
    public int TotalPrepCount 
    { 
     get { return _totalPrepCount; } 
     set { _UpdateField(ref _totalPrepCount, value); } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnCountChanged(int previousValue) 
    { 
     TotalPrepCount = SampleCount + CalibratorCount + ControlCount; 
    } 

    protected void _UpdateField<T>(ref T field, T newValue, 
     Action<T> onChangedCallback = null, 
     [CallerMemberName] string propertyName = null) 
    { 
     if (EqualityComparer<T>.Default.Equals(field, newValue)) 
     { 
      return; 
     } 

     T oldValue = field; 

     field = newValue; 
     onChangedCallback?.Invoke(oldValue); 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

注:

  • 上記のクラスは、4つのプロパティ、我々はを追跡する値ごとに1つのプロパティがあります。
  • 3つのプロパティは単なる値のコンテナです。それらが変更されたときに呼び出されるコールバックメソッドがあります。そのメソッドでは、コードは4番目のプロパティを3つの合計に設定します。
  • INotifyPropertyChangedインターフェイスには、ただ1つのメンバー、PropertyChangedイベントがあります。 MVVMスタイルのコードを扱うときには、実際にこのイベントを実装する基本クラスと、上記の_UpdateField()メソッドのようなヘルパーメソッドを持つと便利です。これらのプロパティーセッターは、それぞれのプロパティに必要な繰り返しロジックを処理するために呼び出すことができます。上記の例では、例を簡略化するために、このロジックをすべて1つのクラスにまとめましたが、適切な基本クラスを維持することをお勧めします(私と他の多くの人がVisual Studioでスニペットを設定しましたこのボイラープレートコードをプロジェクトに簡単に挿入できます)。その定義されたビューモデルで

、XAMLは、このような何かに見えるように簡略化されています

<Window x:Class="TestSO45170241SliderExample.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:l="clr-namespace:TestSO45170241SliderExample" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
    <l:ViewModel/> 
    </Window.DataContext> 

    <Grid> 
    <Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold" 
      HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/> 
    <Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0" 
      VerticalAlignment="Top" FontStyle="Italic"/> 
    <Slider HorizontalAlignment="Left" Margin="181,14,0,0" 
      VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64" 
      Value="{Binding SampleCount}" IsSnapToTickEnabled="True"/> 
    <TextBlock HorizontalAlignment="Left" Margin="165,16,0,0" 
       TextWrapping="Wrap" Text="{Binding SampleCount}" VerticalAlignment="Top"/> 
    <Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0" 
      VerticalAlignment="Top" FontStyle="Italic"/> 
    <Slider HorizontalAlignment="Left" Margin="181,40,0,0" 
      VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7" 
      Value="{Binding CalibratorCount}" IsSnapToTickEnabled="True"/> 
    <TextBlock HorizontalAlignment="Left" Margin="165,42,0,0" 
       TextWrapping="Wrap" Text="{Binding CalibratorCount}" VerticalAlignment="Top"/> 
    <Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0" 
      VerticalAlignment="Top" FontStyle="Italic"/> 
    <Slider HorizontalAlignment="Left" Margin="181,66,0,0" 
      VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4" 
      Value="{Binding ControlCount}" IsSnapToTickEnabled="True"/> 
    <TextBlock HorizontalAlignment="Left" Margin="165,68,0,0" 
       TextWrapping="Wrap" Text="{Binding ControlCount}" VerticalAlignment="Top"/> 
    <Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left" 
      Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/> 
    <TextBlock HorizontalAlignment="Left" Margin="225,110,0,0" 
       FontWeight="Bold" FontStyle="Italic" Text="{Binding TotalPrepCount}" 
       VerticalAlignment="Top"/> 
    </Grid> 
</Window> 

を(脇:MVVMのアプローチをサポートするために変更すること以外に、私はあなたの基本は変更されませんでしたWPFのさまざまなレイアウトコンテナと要素のスタイリング機能を利用する方法は、親しみを持たせることがまず必要なことだと私は同意しますが、ここでそれらを紹介すると、問題が混乱するだけです。元のUIはほとんど変わっていませんが、元々のものとは異なるものに集中でき、賭けを理解するのに役立ちますER伸延なし側面を結合データ。)この実装で

は、全くMainWindow.xaml.csファイルに追加ないコードは存在しません。そのファイルにあるのはコンストラクタのInitializeComponent()へのデフォルト呼び出しで、Visual StudioのWPFプロジェクト用テンプレートから提供されます。

XAMLでは、イベントハンドラサブスクリプションをSlider.Valueプロパティに直接バインドしました。 TextBlock.Textプロパティも同じプロパティにバインドされていることにも注意してください。このようにしてWPFは、スライダ値をビジネスロジックのみのデータ構造に格納し、ビュー内のテキストフィールドにそれらの値を再表示するという重い作業をすべて行います。ビューモデルはint値を格納しますが、スライダはdoubleを使用し、テキストブロックはもちろんstringを使用します。

もちろん、TotalPrepCountフィールドは、表示のために興味のあるプロパティにも拘束されています。

最後に、私も自分の単純な例では、あなたは1本のデータバインディングアプローチを適用したい場合があり、追加の場所を持っていることに注意しましょう。特に、スライダにはそれぞれ最大値があり、ビューにハードコードされています。 MVVMの目的は、ビューがビジネスロジックについての知識をにカプセル化する必要がないことです。これには、許可される値のすべての範囲(*)を知る必要がないことも含まれます。

だから、あなたのビューモデルはまた、例えばかもしれませんSlider.MaximumプロパティとLabel.Contentプロパティの両方にバインドされているMaxSampleCountプロパティです。後者の場合、Binding.StringFormatプロパティを使用して、値を適切なテキストに組み込むことができます。たとえば:

<Label Content="{Binding MaxSampleCount, StringFormat=Patient Samples (max {0})" ... /> 

私は容易にネイティブのWin32コントロールのようなUI APIを使用しての何年も何年も後に、MFC、Windowsフォーム、JavaのAPIを(スイング、AWT、私が最初にWPFを使用しようとし始めたときにすることを認めますSWT)、そしてMac OSのCocoaフレームワーク(データバインディングの形式を使用していますが、XAMLのような宣言的UIは使用していません)でも、私は、他のすべてのAPIで使用されている手続き型アプローチから、 WPFで使用される宣言的および手続き的アプローチ。

しかし、私は、ほとんどの場合、密最高で書かれているMSDNで提供されるドキュメントから厳密にそれを学ぶために、単なる従うことが難しく、多くの場合、完全に役に立たないしようとしていました。誰かが私に、私が上に示したような例を見せてくれたなら(そして彼らは存在していましたが、元気でした;私はどこにいたのかわかりませんでした)、MVVMの基本的なアプローチがどれほど簡単か分かりました。そのアプローチに従えば、WPFプログラムを書くことがどれほど迅速にできるのでしょうか。

私は上記のあなたがWPFの生産的使用のために軌道に乗ることができますし、分かりやすい方法で、あなたの基本を示したいと考えています。

+0

ありがとうございました!それはすべて動作するようです!私は、上記で説明した他のソリューションをテストして、もう少し詳しく知ることができるかどうかを確認します。もう一度ありがとうございます。私はできるだけ早くMVVMのアプローチ全体を検討します。 – WSU

2

私は個人的にこれを行います。しかし、もしあなたがそれを好きなら、@ JerryNixonのanswer here upvoteに行ってください。これは基本的に彼の例の代わりにスライダーを使うことを再考したものです。通常、私はあなたにそれを直接指摘していただけですが、何かを始めたばかりで、PoCがより有用であることが分かっているとき、どのようになっているのかを知っています。とにかく

、ここでyaはXAML

enter image description here

...開始するには、かなりの画像を行きます。

<Window.Resources> 
    <local:SumConverter x:Key="MySumConverter" /> 
</Window.Resources> 
<Grid> 


    <StackPanel VerticalAlignment="Center"> 
     <StackPanel.Resources>    
      <Style TargetType="Slider"> 
       <Setter Property="Margin" Value="10"/> 
       <Setter Property="Width" Value="200"/> 
       <Setter Property="Minimum" Value="0"/> 
       <Setter Property="Maximum" Value="100"/> 
      </Style> 
     </StackPanel.Resources> 

     <Slider x:Name="Slider1"></Slider> 
     <Slider x:Name="Slider2"></Slider> 
     <Slider x:Name="Slider3"></Slider> 

     <TextBlock HorizontalAlignment="Center" TextAlignment="Center"> 
      <Run Text="{Binding Value, ElementName=Slider1}"/> 
      <LineBreak/><LineBreak/> 
      <Run Text="{Binding Value, ElementName=Slider2}"/> 
      <LineBreak/><LineBreak/> 
      <Run Text="{Binding Value, ElementName=Slider3}"/> 
      <LineBreak/> 
      <Run Text="______________________"/> 
      <LineBreak/><LineBreak/> 
      <Run> 
       <Run.Text> 
        <MultiBinding Converter="{StaticResource MySumConverter}" 
            StringFormat="{}{0:C}" 
            FallbackValue="Error" TargetNullValue="Null"> 
         <Binding Path="Value" ElementName="Slider1"/> 
         <Binding Path="Value" ElementName="Slider2"/> 
         <Binding Path="Value" ElementName="Slider3"/> 
        </MultiBinding> 
       </Run.Text> 
      </Run> 
     </TextBlock>    

    </StackPanel> 


</Grid> 

The Converter;

public class SumConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture) 
    { 
     double _Sum = 0; 
     if (values == null) 
      return _Sum; 
     foreach (var item in values) 
     { 
      double _Value; 
      if (double.TryParse(item.ToString(), out _Value)) 
       _Sum += _Value; 
     } 
     return _Sum; 
    } 

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

これがうまくいくことを願っています。

+1

素敵でクリーンなコード – tabby

+0

ローカル変数、アンダースコア+パスカルのちょっと変わった名前ですか? Camelはより標準的です(msdnの例を見てください、[here](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions))。 – Sinatr

+0

ありがとうございました!私の問題に対する2つの解決法が与えられており、両方とも非常に便利です!ニース画像btw;) – WSU

関連する問題