1

の代わりにを使用するようにListBoxオーバーライドするに取り組んでいます。 キャンバス上のそれぞれの外観と位置を定義するコンバーターを使用する があります。がバインドされているコレクションにアイテムを追加するときに、他の をキャンバスに追加できるようにしたいと考えています。のコンバータの外でこれを達成できますか?ItemsPanelTemplateCanvasStackPanelListBoxItemsDataTemplateListBoxItemListBoxUIElementListBoxItem

私のリソースセクションは次のようなものです:

    <Style TargetType="ListBox">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <Canvas x:Key="cnvAwesome">

                    </Canvas>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="ListBoxItem">
        <Setter Property="Canvas.Left" Value="{Binding Path=Position.X}" />
        <Setter Property="Canvas.Top" Value="{Binding Path=Position.Y}" />
    </Style>

そしてリストボックス:

    <ListBox x:Name="lstAwesome" ItemsSource="{Binding Source={StaticResource someCollection}}"/>

ListBox がバインドされているコレクション:

public ObservableCollection<SomeType> someCollection {get; set; }

したがって、ListBox lstAwesome がバインドされているコレクション someCollection にアイテムを追加するメソッドがある場合:

private void AddItemToDataboundCollection(SomeType someItem)
{
    someCollection.Add(someItem)
    // I'd like to add a UIElement to the canvas that the ListBox uses to layout items here.
}

では、データバインドされたリストボックスによって ItemsPanel に使用されているキャンバスに UIElements を追加できますか? そのListBoxがバインドされているコレクションにアイテムを追加するとき、プログラムで?

ご意見をいただければ幸いです。

4

2 に答える 2

2

にアイテムを追加するために、質問にすべてが揃っていると思いCanvasますListBox。質問のコードを使用して簡単な WPF テスト アプリを作成したところ、うまくいきました。ObservableCollectionのにバインドされている にアイテムを追加するたびにItemsSourceListBoxWPF はListBoxItemそのオブジェクトの を作成し、 のスタイルに基づいてListBoxItem、 によってアイテムが正しい位置に描画されますCanvas

上記のコードを機能させることができませんでしたか? その場合、どのようなエラーが発生しましたか、または期待どおりに機能しなかったのは何ですか?

編集:質問をもう一度読んだ後、混乱はWPFバインディングの仕組みの誤解によるものだと思います。ListBoxコレクションをのにバインドする場合、そのコレクションに項目を追加するだけです (コレクションが を実装してItemsSourceいると仮定すると)。WPF は、そのインターフェイスによって提供されるイベントをサブスクライブし、リストに加えられた変更に基づいて作成/破棄します。INotifyCollectionChangedObservableCollectionListBoxItems

編集2:アイテムがコレクションに追加されたときに行が自動的に追加されるように、これには a を使用する方が良いでしょうがDataTemplate、以前に追加されたアイテムの位置を取得する方法がわかりません行は正しい位置から始まります。

機能する可能性のあるオプションの 1 つは、 をサブクラス化Canvasし、それを の として設定することItemsPanelTemplateですListBox。コントロールが追加されるたびに呼び出されるオーバーライドできる関数があると思います。その関数では、以前に追加されたコントロール (Canvasサブクラスにキャッシュされる) の位置を取得し、ライン コントロールを のコントロールにCanvas直接追加することができます。これは、 のコントロールの位置が変更できないことを前提としてCanvasいます。可能であれば、おそらくオブジェクトのPropertyChangedイベントをサブスクライブし、それに応じて行を更新する必要があります。

編集 3 : バインディングは要件によりオプションのようには見えないため、最も悪いオプションは、子を直接追加することです。これを行うには、 VisualTreeHelper.GetChild()を使用してビジュアル ツリーを検索します。

private T FindChild<T>(FrameworkElement parent, string name)
    where T : FrameworkElement
{
    FrameworkElement curObject = null;
    T obj = default(T);
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for(int i = 0; i < count; i++)
    {
        curObject = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
        obj = curObject as T;
        if(null != obj && obj.Name.Equals(name))
        {
            break;
        }

        obj = FindChild<T>(curObject, name);
        if(null != obj)
        {
            break;
        }
    }

    return obj;
}

次のように使用します。

Canvas canvas = FindChild<Canvas>(lstAwesome, "cnvAwesome");

parentこのコードは、指定された型と名前のコントロールをのビジュアル ツリーから再帰的に検索します。ここで使用する方がよいでしょうDependencyObject(FrameworkElementこれが によって返されるためVisualTreeHelper.GetChild())。ただし、Nameは でのみ定義されていFrameworkElementます。

凝りたい場合は、これを拡張メソッドにすることもできます。

于 2009-05-28T16:48:03.297 に答える
1

次のように を設定して、アイテムの表示方法を指定しますItemTemplate

<ListBox.ItemTemplate>
    <DataTemplate>
        <Rectangle Width="{Binding Width}" Height="{Binding Height}" />
    </DataTemplate>
</ListBox.ItemTemplate>

ItemTemplateアイテムに基づいて を変更したい場合は、 を設定して、プログラムで使用ItemTemplateSelectorするものを決定できItemTemplateます。

于 2009-05-28T13:23:34.910 に答える