كيفية تعيين CommandTarget لـ MenuItem داخل قائمة السياق؟

StackOverflow https://stackoverflow.com/questions/616206

  •  03-07-2019
  •  | 
  •  

سؤال

(هذا السؤال يتعلق ب واحدة أخرى, ، ولكنها مختلفة بما فيه الكفاية بحيث أعتقد أنها تستحق وضعها هنا.)

وهنا (مقصوص بشدة) Window:

<Window x:Class="Gmd.TimeTracker2.TimeTrackerMainForm"
    xmlns:local="clr-namespace:Gmd.TimeTracker2"
    xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
    x:Name="This"
    DataContext="{Binding ElementName=This}">
    <Window.CommandBindings>
        <CommandBinding Command="localcommands:TaskCommands.ViewTaskProperties" 
                        Executed="HandleViewTaskProperties" 
                        CanExecute="CanViewTaskPropertiesExecute" />
    </Window.CommandBindings>
    <DockPanel>
<!-- snip stuff -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
<!-- snip more stuff -->
            <Button Content="_Create a new task" Grid.Row="1" x:Name="btnAddTask" Click="HandleNewTaskClick" />
        </Grid>
    </DockPanel>
</Window>

وهنا (مقصوص بشدة) UserControl:

<UserControl x:Class="Gmd.TimeTracker2.TaskStopwatchControl"
             xmlns:local="clr-namespace:Gmd.TimeTracker2"
             xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
             x:Name="This"
             DataContext="{Binding ElementName=This}">
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
                      CommandTarget="What goes here?" />
        </ContextMenu>
    </UserControl.ContextMenu>
    <StackPanel>
        <TextBlock MaxWidth="100" Text="{Binding Task.TaskName, Mode=TwoWay}" TextWrapping="WrapWithOverflow" TextAlignment="Center" />
        <TextBlock Text="{Binding Path=ElapsedTime}" TextAlignment="Center" />
        <Button Content="{Binding Path=IsRunning, Converter={StaticResource boolToString}, ConverterParameter='Stop Start'}" Click="HandleStartStopClicked" />
    </StackPanel>
</UserControl>

من خلال تقنيات مختلفة، أ UserControl يمكن إضافتها بشكل ديناميكي إلى Window.ربما عبر الزر الموجود في النافذة.ربما يكون الأمر الأكثر إشكالية هو من مخزن الدعم المستمر عند بدء تشغيل التطبيق.

كما يتبين من xaml، قررت أنه من المنطقي بالنسبة لي أن أحاول استخدام الأوامر كوسيلة للتعامل مع العمليات المختلفة التي يمكن للمستخدم تنفيذها Taskس.أفعل هذا بهدف نهائي يتمثل في تحليل كل منطق الأوامر إلى طبقة تحكم محددة بشكل أكثر رسمية، لكنني أحاول إعادة البناء خطوة بخطوة.

المشكلة التي أواجهها تتعلق بالتفاعل بين الأمر في ملف UserControlContextMenu والأمر CanExecute, ، المحددة في النافذة.عندما يبدأ التطبيق لأول مرة وتتم استعادة المهام المحفوظة إلى TaskStopwatches في النافذة، لا يتم تحديد أي عناصر فعلية لواجهة المستخدم.إذا قمت بعد ذلك بالنقر فوق الزر r على الفور UserControl في ال Window في محاولة لتنفيذ ViewTaskProperties الأمر، CanExecute لا يعمل المعالج مطلقًا ويظل عنصر القائمة معطلاً.إذا قمت بعد ذلك بالنقر فوق بعض عناصر واجهة المستخدم (على سبيل المثال، الزر) فقط للتركيز على شيء ما، فإن CanExecute يتم تشغيل المعالجات باستخدام CanExecuteRoutedEventArgsتم تعيين خاصية المصدر على عنصر واجهة المستخدم الذي يحتوي على التركيز.

في بعض النواحي، يبدو أن هذا السلوك معروف - لقد تعلمت أن القوائم ستوجه الحدث عبر العنصر الذي تم التركيز عليه آخر مرة لتجنب إرسال الحدث دائمًا من عنصر القائمة.لكن ما أعتقد أنني أريده هو أن يكون مصدر الحدث هو عنصر التحكم نفسه، أو المهمة التي يلتف حولها عنصر التحكم (ولكن Task ليس عنصرًا، لذلك لا أعتقد أنه يمكن أن يكون مصدرًا).

اعتقدت أنه ربما كنت في عداد المفقودين CommandTarget الملكية على MenuItem في ال UserControl, ، وكان أول ما فكرت به هو أنني أريد أن يأتي الأمر من UserControl، لذلك من الطبيعي أن أحاول أولاً:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding ElementName=This}" />

فشل هذا باعتباره ربطًا غير صالح.لست متأكدا لماذا.ثم فكرت، "هممم، أنا أبحث عن الشجرة، لذلك ربما ما أحتاجه هو مصدر نسبي" وحاولت هذا:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" />

لقد فشل ذلك أيضًا، ولكن عندما نظرت إلى xaml الخاص بي مرة أخرى، أدركت أن ContextMenu موجود في خاصية UserControl، وهو ليس عنصرًا فرعيًا.لذلك خمنت (وفي هذه المرحلة كان تخمينًا):

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" />

وهذا فشل أيضاً.

إن فشل التخمين والتحقق من هذا القبيل يكفي لجعلي أتراجع وأدرك أنني أفتقد نوعًا من المفهوم الأساسي هنا.اذا ماذا افعل؟

  1. هل فهمي هو:دور CommandTarget صحيح أن هذا يوفر آلية لتعديل مصدر الأمر؟
  2. كيف أربط من أ MenuItem في UserControl.ContextMenu إلى التملك UserControl؟أم أنني أفعل شيئًا خاطئًا لمجرد أنني أشعر بالحاجة إلى ذلك؟
  3. هل رغبتي في تعيين سياق الأمر بواسطة العنصر الذي تم النقر عليه لإنشاء قائمة السياق، على عكس العنصر الذي تم التركيز عليه قبل قائمة السياق، غير صحيحة؟ربما أحتاج إلى كتابة الأمر الخاص بي بدلاً من استخدام الأمر RoutedUICommand:

    private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
    public static RoutedUICommand ViewTaskProperties
    {
        get { return viewTaskPropertiesCommand; }
    }
    
  4. هل هناك خلل أساسي أعمق في تصميمي؟هذا هو أول مشروع مهم لي في WPF، وأنا أفعل ذلك في وقتي الخاص كتجربة تعليمية، لذلك أنا بالتأكيد لا أعارض تعلم بنية حلول متفوقة.

هل كانت مفيدة؟

المحلول

1:نعم، CommandTarget يتحكم في المكان الذي يبدأ منه RoutedCommand التوجيه.

2: قائمة السياق لديه PlacementTarget الخاصية التي ستسمح بالوصول إلى UserControl الخاص بك:

<MenuItem x:Name="mnuProperties" Header="_Properties"
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
          CommandTarget="{Binding PlacementTarget,
                                  RelativeSource={RelativeSource FindAncestor,
                                                                 AncestorType={x:Type ContextMenu}}}"/>

لتجنب تكرار هذا في كل MenuItem يمكنك استخدام Style.

3 و 4:أود أن أقول أن رغبتك معقولة.نظرًا لأن معالج التنفيذ موجود على النافذة، فلا يهم الآن، ولكن إذا كان لديك مناطق مختلفة من التطبيق، ولكل منها معالج التنفيذ الخاص بها لنفس الأمر، فسيكون من المهم مكان التركيز.

نصائح أخرى

الحل المماثل الذي وجدته هو استخدام خاصية Tag الخاصة بالوالد للحصول على سياق البيانات:

<Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.ContextMenu>
        <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
            <MenuItem 
                Header="{Binding Path=ToolbarDelete, Mode=Default, Source={StaticResource Resx}}" 
                Command="{Binding RemoveCommand}" 
                CommandParameter="{Binding DataContext.Id, RelativeSource={RelativeSource TemplatedParent}}"/>
        </ContextMenu>
    </Grid.ContextMenu>

    <TextBlock Text="{Binding Name}" Padding="2" />

</Grid>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top