2

仕様に合わせてWPFでUIを構築しようとしています。UIは、アイテムのコレクションを編集するためのものです。各アイテムには、編集可能な文字列プロパティと、UIに表示する必要のある可変数の読み取り専用文字列があります。次のようになります。

                                  ここに画像の説明を入力してください

または、データに応じて、テキストラベル列の数が異なる場合があります。

                                      ここに画像の説明を入力してください

テキスト列の数は完全に可変であり、1から「ロット」まで変化する可能性があります。仕様では、列のサイズを最長のエントリに合わせて指定する必要があり(常に非常に短い)、全体がグリッドのように見える必要があります。このグリッドはウィンドウに含まれ、ウィンドウに合わせてテキストボックスを水平方向に引き伸ばします。

重要なのは、テキストボックスに複数行のテキストを含めることができ、テキストに合わせて自動的に拡大することです。その場合は、下の行を邪魔にならないようにプッシュする必要があります。

質問:WPFでこれを行うための良い方法は何でしょうか?

WinFormsのバックグラウンドから来て、TableLayoutPanel私が書いたコードによって直接入力される、を考えています。ただし、これはWPFで行う必要があります。それでも自分自身を取得Gridしてコードに入力することはできますが、WPFでの処理方法に沿った方法、つまり、ViewModelを定義して入力し、ビュー全体をXAMLで記述する方法を使用したいと思います。 。ただし、XAMLでそのようなビューを記述する方法は考えられません。

MVVMとXAMLを使用してこれに最も近い方法は、ItemsControl行ごとに1つのアイテムを使用し、データテンプレートを使用することです。データテンプレートはItemsControl、可変数のラベルに別の(今回は水平にスタックされた)データテンプレートを使用し、その後にテキストが続きます。箱。残念ながら、これは仕様が要求するようにグリッドパターンで垂直に整列させることはできません。

4

6 に答える 6

1

次のコードは、バインディングを設定するとともに、必要に応じてサイズを変更したいコントロールを含むグリッドを構築する方法を示しています。

    public void BuildListTemplate(IEnumerable<Class1> myData, int numLabelCols)
    {
        var myGrid = new Grid();

        for (int i = 0; i < myData.Count(); i++)
        {
            myGrid.RowDefinitions.Add(new RowDefinition() { Height= new GridLength(0, GridUnitType.Auto)});
        }
        for (int i = 0; i < numLabelCols; i++)
        {
            myGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) });
        }
        myGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
        for (int i = 0; i < myData.Count(); i++)
        {
            for (int j = 0; j < numLabelCols; j++)
            {
                var tb = new TextBlock();
                tb.SetBinding(TextBlock.TextProperty, new Binding("[" + i + "].labels[" + j + "]"));
                tb.SetValue(Grid.RowProperty, i);
                tb.SetValue(Grid.ColumnProperty, j);
                tb.Margin = new Thickness(0, 0, 20, 0);
                myGrid.Children.Add(tb);
            }
            var edit = new TextBox();
            edit.SetBinding(TextBox.TextProperty, new Binding("[" + i + "].MyEditString"));
            edit.SetValue(Grid.RowProperty, i);
            edit.SetValue(Grid.ColumnProperty, numLabelCols);
            edit.AcceptsReturn = true;
            edit.TextWrapping = TextWrapping.Wrap;
            edit.Margin = new Thickness(0, 0, 20, 6);
            myGrid.Children.Add(edit);
        }
       contentPresenter1.Content = myGrid;
    }

上記の簡単な説明グリッドを作成し、グリッドの行を定義するだけです。コンテンツのサイズを自動調整するグリッドの一連の列。

次に、データ ポイントごとにコントロールを生成し、バインディング パスを設定し、その他のさまざまな表示属性を割り当て、コントロールの正しい行/列を設定します。

最後に、表示するためにウィンドウ xaml で定義された contentPresenter にグリッドを配置します。

あとは、次のプロパティを持つクラスを作成し、contentPresenter1 のデータ コンテキストをそのオブジェクトのリストに設定するだけです。

public class Class1
{
    public string[] labels { get; set; }
    public string MyEditString { get; set; }
}

完全を期すために、ウィンドウの xaml とコンストラクターを示して、すべてを接続します。

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<ContentPresenter Name="contentPresenter1"></ContentPresenter>
</Window>

    public MainWindow()
    {
        InitializeComponent();

        var data = new List<Class1>();

        data.Add(new Class1() { labels = new string[] {"the first", "the second", "the third"}, MyEditString = "starting text"});
        data.Add(new Class1() { labels = new string[] { "col a", "col b" }, MyEditString = "<Nothing>" });

        BuildListTemplate(data, 3);
        DataContext = data;
    }

もちろん、リストビューなどの他の方法を使用して、グリッドビューを構築することもできます (多数の行がある場合はこれを行います)、または他のそのようなコントロールを使用できますが、特定のレイアウト要件を考えると、おそらく必要になるでしょう。グリッドを使用したこのメソッド。

EDIT : xaml で実行する方法を探していることがわかりました。別々の行で動的にサイズ設定されたコンテンツに合わせて物事を揃える必要がない場合は、より実行可能になります...しかし、コードビハインドを恐れないでください。UIを作成するときにそれが必要です。

于 2012-08-24T23:54:00.370 に答える
1

独自のものを作成してPanelから、その内部に配置された子に対してレイアウト ロジックをどのように機能させるかを決定できます。

インスピレーションのためにこれを見てください:

「ColumnCount」プロパティを設定し、それを and 内で使用して、いつ子をラップするかを決定できMeassureOverrideますArrangeOverride


または、このコードを変更することもできます (Silverlight コードであることはわかっていますが、WPF でもほぼ同じはずです)。

すべての列の幅を同じにする (デフォルトは 1 つ星の "*") 代わりに、必要なサイズの異なる列幅を記録する List/Collection プロパティを追加し、AutoGrid_LayoutUpdatedそれらの幅を使用してColumnDefinition値を作成することができます。

于 2012-08-24T22:51:13.040 に答える
1

コードビハインドでそれを行うことは、実際にはWPFish(wpfの方法)ではありません。ここで私はあなたに私の解決策を提供します。

0) 開始する前に、GridHelpers が必要です。これらは、行/列を動的に変更できることを確認します。ちょっとしたグーグルでそれを見つけることができます:

ItemsPanelTemplate の Grid に RowDefinition を動的に追加するにはどうすればよいですか?

実際に何かを実装する前に、プログラムを少し再構築する必要があります。次のような新しい構造「CustomCollection」が必要です。

  • RowCount - 行数 (INotifyPropertyChanged を使用して実装)
  • ColumnCount - 列の数 (INotifyPropertyChanged を使用して実装)
  • ActualItems - 「行/項目」の独自のコレクション(ObservableCollection)

1) グリッドを保持する ItemsControl を作成することから始めます。Grid RowDefinitions/ColumnDefinitions が動的であることを確認してください。ItemContainerStyle を適用します。

    <ItemsControl 
      ItemsSource="{Binding Collection.ActualItems, 
        Converter={StaticResource presentationConverter}">
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <Grid
             local:GridHelpers.RowCount="{Binding Collection.RowCount}"
             local:GridHelpers.StarColumns="{Binding Collection.ColumnCount, 
               Converter={StaticResource subtractOneConverter}"
             local:GridHelpers.ColumnCount="{Binding Collection.ColumnCount}" />
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
      <ItemsControl.ItemContainerStyle>
         <Style TargetType="{x:Type FrameworkElement}">
           <Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
           <Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
         </Style>
     </ItemsControl.ItemContainerStyle>
    </ItemsControl>

あとは、Viewmodel プレゼンテーションを View プレゼンテーションに変換する presentationConverter を実装するだけです。(読む: http://wpftutorial.net/ValueConverters.html )

コンバーターは、各「ラベル」または「テキストボックス」が個別のエンティティであるアイテムのコレクションを返す必要があります。各エンティティには、RowIndex と ColumnIndex が必要です。

エンティティクラスは次のとおりです。

public class SingleEntity
{
   ..RowIndex property..
   ..ColumnIndex property..
   ..ContentProperty..  <-- This will either hold label string or TextBox binded property.
   ..ContentType..
}

ContentType は、TextBox または Label を作成する必要があるかどうかを決定するために ItemsTemplate でバインドする列挙型であることに注意してください。

これは非常に長い解決策のように思えるかもしれませんが、実際にはいくつかの理由で優れています。

  • ViewModel には何が起こっているのかわかりません。これは純粋にビューの問題です。
  • すべてがダイナミックです。ViewModel で何かを追加/削除するとすぐに (すべてが適切に実装されていると仮定)、ItemsControl はコンバーターを再トリガーし、再度バインドします。そうでない場合は、ActualItems=null を設定してから戻すことができます。

ご不明な点がございましたら、お知らせください。

于 2012-08-25T12:25:08.947 に答える
1

これはうまくマッピングできません。おそらく aDataGridを使用して、このように再テンプレート化できます。他のアプローチでは、レイアウトを正しく行うために列などを強制的に追加する必要がある場合があります。

(にフックしAutoGeneratingColumnて、その 1 つの書き込み可能な列の幅を に設定できます*)

于 2012-08-24T22:41:15.863 に答える
0

あなたはおそらくこの問題をはるかに超えていますが、最近同様の問題が発生し、xamlで驚くほどうまく機能するようになったので、解決策を共有したいと思いました。

主な欠点は、「たくさんの」ラベルが何を意味するかについて、積極的に上限を設定する必要があることです。ロットが100を意味する場合、これは機能しません。Ctrl + Vを入力する回数よりもロットが確実に少ない場合は、これを機能させることができる可能性があります。また、ビューモデルの単一のObservableCollectionプロパティにすべてのラベルを配置することをいとわない必要があります。あなたの質問では、とにかくそれをすでに試しているように思えました。

AlternationIndexラベルのインデックスを取得して列に割り当てるために利用します。ここから学んだと思います。アイテムのラベルがx未満の場合、余分な列が邪魔になりません。アイテムに>xのラベルがある場合、ラベルは互いに積み重ねられ始めます。

<!-- Increase AlternationCount and RowDefinitions if this template breaks -->
<ItemsControl ItemsSource="{Binding Labels}" IsTabStop="False" AlternationCount="5">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                 <TextBlock Text="{Binding}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Grid.Column" 
                        Value="{Binding RelativeSource={RelativeSource Self}, 
                                        Path=(ItemsControl.AlternationIndex)}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid IsItemsHost="True">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="A"/>
                        <ColumnDefinition SharedSizeGroup="B"/>
                        <ColumnDefinition SharedSizeGroup="C"/>
                        <ColumnDefinition SharedSizeGroup="D"/>
                        <ColumnDefinition SharedSizeGroup="E"/>
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
于 2012-12-20T18:52:39.223 に答える
0

簡単ですが、それほど高度ではない方法は、コード ビハインドで UI を動的に埋めることです。これは最も簡単な解決策のようで、winforms の経験と多かれ少なかれ一致します。

MVVM の方法でそれを行いたい場合は、おそらく を使用ItemsControlし、アイテムのコレクションを として設定し、コレクション アイテム タイプにItemsSourceを定義する必要があります。DataTemplate

私はそのDataTemplateようなものを持っています:

<Window x:Class="SharedSG.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:app="clr-namespace:SharedSG"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type app:LabelVM}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="G1"/>
                    <ColumnDefinition SharedSizeGroup="G2"/>
                    <ColumnDefinition MinWidth="40" Width="*"/>
                </Grid.ColumnDefinitions>
                <Label Content="{Binding L1}" Grid.Column="0"/>
                <Label Content="{Binding L2}" Grid.Column="1"/>
                <TextBox Grid.Column="2"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <Grid Grid.IsSharedSizeScope="True">
        <ItemsControl ItemsSource="{Binding}"/>
    </Grid>
</Window>
于 2012-08-24T22:07:45.213 に答える