0

TPTB は、アプリを 1 つのウィンドウで実行する必要があると判断しました。モーダル モードで新しいウィンドウをポップアップすることは許可されていません。

そして当然のことながら、あらゆる場所にモーダル ダイアログをポップアップ表示する UI デザインがあります。

そこで、最上位のグリッドをウィンドウに追加しました。そのグリッドでは、行も列も定義していないため、すべてが行 0/列 0 に描画されます。

グリッドの最初の要素は、ウィンドウに通常表示されるすべてのものを含む別のグリッドでした。2 つ目は、グレーの半透明の背景を持つフルサイズの Border です。残りは、ポップアップとして表示する必要があるさまざまな UserControls を含む、広いマージンと白い背景を持つ Borders でした。最初を除くすべてが Visibility="Collapsed" でした。

そして、ポップアップを表示する必要があるときは、灰色の背景と適切な UserControl に Visibility="Visible" を設定します。その結果、うまく機能する素晴らしいシャドーボックス効果が得られました。

ポップアップがポップアップを表示できるようにする必要があると誰かが判断するまで。予測不可能な順序で。

Grid で Visibility="Collapsed" 要素を使用して実装したメソッドの制限は、それらの順序が固定されていることでした。UserControlB は、UserControlA の表示を要求したのが UserControlB であっても、常に UserControlA の上に表示されます。そして、それは受け入れられません。

そこで私の次の試みは、Window.Resources でさまざまな UserControls を定義し、それらをコードで Grid に追加することでした。

this.masterGrid.Children.Add(this.Resources["userControlA"] as UserControlA);

そして、それはほとんど機能します。しかし、バインディングはすべてめちゃくちゃです。

例として、コントロールの 1 つは、ウィンドウのビューモデルのメンバー オブジェクト内のコレクションの CurrentItem にプロパティをバインドすることになっています。コントロールをグリッドで非表示のアイテムとして定義すると、正常に機能しました。しかし、それをリソースとして定義したとき、プロパティは null で、バインドされませんでした。

グリッドに追加した後、コードでバインドしてみました。

userControlA.SetBinding(UserControlA.myProperty, new Binding()
    { Source = this.viewModel.myCollection.CurrentItem });

そして、それは問題なくコンパイルおよび実行されますが、正しいオブジェクトにバインドしていません。

初めて UserControl を表示すると、適切なオブジェクトがバインドされていることがわかります。しかし、それを閉じて、コレクション内の CurrentItem を別のオブジェクトに移動し、UserControl を再度表示すると、最初のオブジェクトがバインドされていることがわかります。もう一度閉じて 3 回目に開くと、正しいオブジェクトがコントロールにバインドされていることがわかります。

コードをチェックインしましたが、バインドしている CurrentItem は毎回正しいのですが、1 回おきにかかるようです。

だから私は最初に明示的にバインディングをクリアしようとしました:

BindingOperations.ClearBinding(userControlA, UserControlA.myProperty);
userControlA.SetBinding(UserControlA.myProperty, new Binding()
    { Source = this.viewModel.myCollection.CurrentItem });

しかし、それは何の違いもなかったようです。

全体として、かなり単純なはずの問題を解決するために、私はうさぎの穴を駆け下り、複雑さをどんどん深く追い求めているように感じます。

次のことについて何か提案はありますか?

  1. 動的に追加された要素でバインディングを機能させる方法、または
  2. 動的に順序付けされた要素を使用せずに、シャドーボックスとして任意に順序付けされたポップアップを表示する方法は?

前もって感謝します。

4

1 に答える 1

0

新しい Windows を作成できないというのは非常に奇妙に思えますが、ビューを MainWindow のリソースに格納するなどの不要なことを行って、あまり複雑にしないことを強くお勧めします。

これらの要素の新しいインスタンスを ObservableCollection に追加した方がよいでしょう。

XAML:

<Window x:Class="WpfApplication4.Window8"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication4"
        Title="Window8" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:ViewModel1}">
            <StackPanel Background="Green">
                <TextBlock Text="This is ViewModel1!!"/>
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ViewModel2}">
            <StackPanel Background="Blue" HorizontalAlignment="Center">
                <TextBlock Text="This is ViewModel2!!"/>
                <TextBlock Text="{Binding Text2}"/>

            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ViewModel3}">
            <StackPanel Background="Red" VerticalAlignment="Center">
                <TextBlock Text="This is ViewModel3!!"/>
                <TextBlock Text="{Binding Text3}"/>
                <TextBox Text="{Binding Text3}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    <DockPanel>
        <Button Width="100" Content="Add" Click="Add_Click" DockPanel.Dock="Top"/>
        <Button Width="100" Content="Remove" Click="Remove_Click" DockPanel.Dock="Top"/>

        <ListBox ItemsSource="{Binding ActiveWidgets}" SelectedItem="{Binding SelectedWidget}">
            <ListBox.Template>
                <ControlTemplate>
                    <ItemsPresenter/>
                </ControlTemplate>
            </ListBox.Template>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="VerticalAlignment" Value="Stretch"/>
                    <Setter Property="HorizontalAlignment" Value="Stretch"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <ContentPresenter ContentSource="Content"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </DockPanel>
</Window>

コードビハインド:

using System.Linq;
using System.Windows;
using System.Collections.ObjectModel;
using System;

namespace WpfApplication4
{
    public partial class Window8 : Window
    {
        private WidgetsViewModel Widgets { get; set; }

        public Window8()
        {
            InitializeComponent();

            DataContext = Widgets = new WidgetsViewModel();
        }

        private Random rnd = new Random();
        private int lastrandom;
        private void Add_Click(object sender, RoutedEventArgs e)
        {
            var random = rnd.Next(1, 4);
            while (random == lastrandom)
            {
                random = rnd.Next(1, 4);
            }

            lastrandom = random;

            switch (random)
            {
                case 1:
                    Widgets.ActiveWidgets.Add(new ViewModel1() {Text = "This is a Text"});
                    break;
                case 2:
                    Widgets.ActiveWidgets.Add(new ViewModel2() { Text2 = "This is another Text" });
                    break;
                case 3:
                    Widgets.ActiveWidgets.Add(new ViewModel3() { Text3 = "This is yet another Text" });
                    break;
            }

            Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();

        }

        private void Remove_Click(object sender, RoutedEventArgs e)
        {
            Widgets.ActiveWidgets.Remove(Widgets.SelectedWidget);
            Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
        }
    }

    public class WidgetsViewModel: ViewModelBase
    {
        public ObservableCollection<ViewModelBase> ActiveWidgets { get; set; }

        private ViewModelBase _selectedWidget;
        public ViewModelBase SelectedWidget
        {
            get { return _selectedWidget; }
            set
            {
                _selectedWidget = value;
                NotifyPropertyChange(() => SelectedWidget);
            }
        }

        public WidgetsViewModel()
        {
            ActiveWidgets = new ObservableCollection<ViewModelBase>();
        }
    }

    public class ViewModel1: ViewModelBase
    {
        public string Text { get; set; }
    }

    public class ViewModel2: ViewModelBase
    {
        public string Text2 { get; set; }
    }

    public class ViewModel3: ViewModelBase
    {
        public string Text3 { get; set; }
    }
}

コードをコピーしてファイル - 新規 - WPF アプリケーションに貼り付けるだけで、結果を自分で確認できます。

グリッドは常に最後に追加された UI 要素を一番上に配置するため、observablecollection に項目を追加すると、これらの「異なるウィジェット」が常に互いの上に表示され、最後に追加されたウィジェットが一番上に表示されることがわかります。

WidgetA肝心なのは、 open を要求するときはWidgetB、新しいものを作成してコレクションWidgetBViewModelに追加するだけです。ActiveWidgetsその後、WidgetB不要になったら削除します。

次に、ViewModel ごとに適切な DataTemplate 内に UserControls を配置するだけです。ウィジェットごとに個別の ViewModel を保持することを強くお勧めします。ウィジェット間でデータを共有する必要がある場合は、ViewModel 間でデータを共有するだけです。正当な理由がListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" ない限り、次のようなことを試みないでください。

こうすれば、物を扱う必要がなくなりますPanel.ZIndexVisibilityおそらく、いくつかの添付プロパティを作成して、フォーカスなどを処理することができますが、このアプローチは非常に単純であり、および のアプローチよりもはるかにパフォーマンスが優れていResourcesます。

于 2013-02-15T22:06:35.037 に答える