上下文菜单PlacementTarget属性

时间:2015-04-15 18:18:34

标签: wpf xaml mvvm data-binding caliburn.micro

我的智慧结束了,这让我非常沮丧。

在我的计划中有a)ListBox显示位置列表,b)TabControl显示从上述ListBox中选择的位置的详细信息 两者都有一个上下文菜单,其中包含绑定到ViewModel上的方法的各种操作。但是,当点击ContextMenu中的项目时,我会得到一个异常,告诉我,找不到任何方法。

我知道ContextMenu不是可视化树的一部分,因此正常的数据绑定不起作用。因此,我必须使用PlacementTarget属性。 这是我的问题:1。VS 2013设计师甚至不认识这个属性并不断告诉我他无法解决它。 它根本不起作用,我不明白为什么。

我使用Caliburn.Micro作为我的MVVM Framework和Blend样式代码来将XAML连接到我的VM属性。我尝试了一切,我要么得到一个例外,要么在另一个ViewModel上调用该方法(我用一个来显示一个位置的细节)

这是我的usercontrol:

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:vm="clr-namespace:RpgTools.LocationPresenter.ViewModels"
             x:Class="RpgTools.LocationPresenter.Views.LocationView"
             mc:Ignorable="d"
             d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type={x:Type vm:LocationViewModel}}"
             cal:Bind.AtDesignTime="True"
             Padding="5">
    <Grid>  
             <!-- Code left out for brevity -->
    <ScrollViewer Margin="5,3,3,5"
                          DockPanel.Dock="Top">
                <ListBox x:Name="Locations"
                         SelectedItem="SelectedLocation"
                         ItemsSource="{Binding Locations}"
                         Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <StackPanel.ContextMenu>
                                    <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag}">
                                        <MenuItem Header="Delete">
                                            <i:Interaction.Triggers>
                                                <i:EventTrigger EventName="Click">
                                                    <cal:ActionMessage MethodName="Close" />
                                                </i:EventTrigger>
                                            </i:Interaction.Triggers>
                                        </MenuItem>
                                    </ContextMenu>
                                </StackPanel.ContextMenu>
                                <TextBlock Text="{Binding Value.Name}" />
                                <TextBlock Text="{Binding Key, StringFormat=' (\{0\})'}" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="MouseDoubleClick">
                            <cal:ActionMessage MethodName="OpenLocationTab">
                                <cal:Parameter Value="{Binding SelectedItem, ElementName=Locations}" />
                            </cal:ActionMessage>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>

                </ListBox>
            </ScrollViewer
        <TabControl x:Name="Items"
                    Grid.Column="2"
                    Grid.Row="0"
                    Margin="3,5,5,3"
                    Visibility="{Binding TabControlVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal"
                                x:Name="TabHeaderStackPanel">
                        <StackPanel.ContextMenu>
                            <ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext}">
                                <MenuItem Header="Close This"
                                          cal:Message.Attach="Close()" />
                            </ContextMenu>
                        </StackPanel.ContextMenu>
                        <TextBlock Text="{Binding DisplayName}" />
                        <Button Padding="10,0,0,0"
                                Content="X"
                                Style="{DynamicResource NoChromeButton}"
                                cal:Message.Attach="CloseTab($dataContext)" />
                    </StackPanel>
                </DataTemplate>
            </TabControl.ItemTemplate>
        </TabControl>
    </Grid>
</UserControl>

以上是我的ViewModel视图:

namespace RpgTools.LocationPresenter.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.Linq;
    using Caliburn.Micro;
    using PropertyChanged;
    using RpgTools.Core;
    using RpgTools.Core.Common;
    using RpgTools.Core.Contracts;
    using RpgTools.Core.Models;
    using RpgTools.Core.Models.Locations;
    using RpgTools.Locations;

    /// <summary>Represents the location view model.</summary>
    [ImplementPropertyChanged]
    [RpgModuleMetadata(Name = "Locations")]
    [Export(typeof(IRpgModuleContract))]
    public class LocationViewModel : Conductor<LocationDetailsViewModel>.Collection.OneActive, IRpgModuleContract
    {
        /// <summary>Infrastructure. Holds a reference to the  location repository.</summary>
        private readonly ILocationRepository locationRepository;

        /// <summary>Infrastructure. Holds a reference to the  window manager.</summary>
        private readonly IWindowManager windowManager;

        /// <summary>Infrastructure. Holds a reference to the event aggregator.</summary>
        private readonly IEventAggregator eventAggregator;

        /// <summary>Initialises a new instance of the <see cref="LocationViewModel"/> class.</summary>
        public LocationViewModel()
        {
            if (Execute.InDesignMode)
            {
                this.LocationSelectorVisible = true;
                this.SaveButtonVisible = true;
                this.TabControlVisible = true;

                this.CheckListItems = new ObservableCollection<CheckListItem>
                                      {
                                          new CheckListItem("All", true),
                                          new CheckListItem("Location", false)
                                      };
                var guids = new[] { Guid.NewGuid(), Guid.NewGuid() };

                this.Locations = new DictionaryRange<Guid, Location>
                                 {
                                     {
                                         guids[0],
                                         new City
                                         {
                                             Id = guids[0],
                                             Name = "Stromsang",
                                             Description = "A City"
                                         }
                                     },
                                     {
                                         guids[1],
                                         new Location
                                         {
                                             Id = guids[1],
                                             Name = "Forst",
                                             Description = "A place with trees"
                                         }
                                     }
                                 };
            }
        }

        /// <summary>Initialises a new instance of the <see cref="LocationViewModel"/> class.</summary>
        /// <param name="eventAggregator">The event aggregator.</param>
        /// <param name="windowManager">The window manager.</param>
        [ImportingConstructor]
        public LocationViewModel(IEventAggregator eventAggregator, IWindowManager windowManager)
        {
            this.locationRepository = new LocationRepositoryFactory(new DatabaseSeviceClient()).ForDefaultCulture();
            this.eventAggregator = eventAggregator;
            this.windowManager = windowManager;

            // Set the initial visibilities of controls.
            this.LocationSelectorVisible = true;
            this.SaveButtonVisible = false;
            this.TabControlVisible = false;

            // Generate the check list items
            this.CheckListItems = new ObservableCollection<CheckListItem>();
            this.CheckListItems.Insert(0, new CheckListItem(Constants.AllOptions, true));
        }

        /// <summary>Gets or sets a value indicating whether the location selector is visible.</summary>
        public bool LocationSelectorVisible { get; set; }

        /// <summary>Gets or sets a value indicating whether the save button is visible.</summary>
        public bool SaveButtonVisible { get; set; }

        /// <summary>Gets or sets a value indicating whether the tab control is visible.</summary>
        public bool TabControlVisible { get; set; }

        /// <summary>Gets or sets the locations.</summary>
        public IDictionaryRange<Guid, Location> Locations { get; set; }

        /// <summary>Gets or sets the list of check list items.</summary>
        public ObservableCollection<CheckListItem> CheckListItems { get; set; }

        /// <summary>Toggles the visibility of the location list.</summary>
        public void ToggleLocationList()
        {
            this.LocationSelectorVisible = !this.LocationSelectorVisible;
        }

        /// <summary>Opens a location tab in the tab control.</summary>
        /// <param name="location">The location.</param>
        public void OpenLocationTab(KeyValuePair<Guid, Location> location)
        {
            if (this.Items.All(s => s.DisplayName != location.Value.Name) && location.Value != null)
            {
                // Make the tab control viosible to the user.
                this.TabControlVisible = true;

                this.ActivateItem(new LocationDetailsViewModel(this.eventAggregator, this.windowManager)
                                  {
                                      Location = location.Value
                                  });

                this.LocationSelectorVisible = false;
                this.SaveButtonVisible = true;
            }
        }

        /// <summary>Closes a tab from the location tab control.</summary>
        /// <param name="detailsViewModel">The data context the tab belongs to.</param>
        public void CloseTab(LocationDetailsViewModel detailsViewModel)
        {
            this.CloseItem(detailsViewModel);

            if (!this.Items.Any())
            {
                this.TabControlVisible = false;
                this.LocationSelectorVisible = true;
                this.SaveButtonVisible = false;
            }
        }

        /// <summary>Closes all location tabs.</summary>
        /// <param name="locations">The locations to close.</param>
        public void CloseTabs(ICollection<LocationDetailsViewModel> locations)
        {
            foreach (LocationDetailsViewModel viewModel in locations)
            {
                this.CloseTab(viewModel);
            }
        }

        /// <summary>Closes a collection of tabs, except one.</summary>
        /// <param name="locations">The locations to close.</param>
        /// <param name="location">The location to except from closing.</param>
        public void CloseTabsExcept(ICollection<LocationDetailsViewModel> locations, LocationDetailsViewModel location)
        {
            foreach (LocationDetailsViewModel viewModel in locations.Where(vm => vm.DisplayName != location.DisplayName))
            {
                this.CloseTab(viewModel);
            }
        }

        /// <summary>Loads the location ids from the location repository.</summary>
        public void LoadLocations()
        {
            this.Locations = this.locationRepository.FindAll();
        }

        /// <summary>Loads the locations types and updates the filter list.</summary>
        public void LoadLocationTypes()
        {
            IEnumerable<string> types = this.Locations.Values.Select(l => l.GetType().Name);

            types.Where(t => this.CheckListItems.All(i => i.Name != t)).ToList().ForEach(t => this.CheckListItems.Add(new CheckListItem(t, true)));
        }

        /// <summary>Filters the locations based on the checked check boxes.</summary>
        public void FilterLocations()
        {
            // Check if the locations have been loaded.
            // if not just return.
            if (this.Locations == null)
            {
                return;
            }

            // If the all item is checked, we can safely return all locations without filtering.
            if (this.CheckListItems.Any(i => (i.Name == Constants.AllOptions && (i.IsChecked != null && (bool)i.IsChecked))))
            {
                this.Locations = this.locationRepository.FindAll();
                return;
            }

            // Get the checked items.
            var checkedBoxes = this.CheckListItems.Where(i => (i.IsChecked != null && (bool)i.IsChecked));

            this.Locations = new DictionaryRange<Guid, Location>(this.locationRepository.FindAll().Where(l => checkedBoxes.Any(b => b.Name == l.Value.GetType().Name)).ToDictionary(x => x.Key, x => x.Value));
        }

        /// <summary>Toggles check list item.</summary>
        /// <param name="sender">The item that changed.</param>
        /// <param name="isChecked">The state of the item.
        /// </param>
        public void FilterChecklist(string sender, bool isChecked)
        {
            // Check if the locations have been loaded.
            // if not just return.
            if (this.Locations == null)
            {
                return;
            }

            // Check if the sender was the "all" item.
            if (sender == Constants.AllOptions)
            {
                // Set all items to be set if "all" was checked.
                // Otherwise set all items to be unchecked.
                if (isChecked)
                {
                    foreach (CheckListItem item in this.CheckListItems)
                    {
                        item.IsChecked = true;
                    }
                }
                else
                {
                    foreach (CheckListItem item in this.CheckListItems)
                    {
                        item.IsChecked = false;
                    }
                }
            }
            else
            {
                // Since we didn't change the all item 
                // we have to check if all others are checked
                // to determine the correct state of the all item.
                var allItem = this.CheckListItems.Single(i => i.Name == Constants.AllOptions);

                var itemsWithoutAll = new List<CheckListItem>(this.CheckListItems.Where(i => i != allItem));

                if (itemsWithoutAll.All(i => i.IsChecked != null && (bool)i.IsChecked))
                {
                    allItem.IsChecked = true;
                }
                else if (itemsWithoutAll.Any(i => i.IsChecked != null && (bool)i.IsChecked))
                {
                    allItem.IsChecked = null;
                }
                else
                {
                    allItem.IsChecked = false;
                }
            }
        }

        /// <summary>Saves a location to the repository.</summary>
        /// <param name="viewModel">The view model with the location to be saved.</param>
        public void SaveLocation(LocationDetailsViewModel viewModel)
        {
            // Get the location to save
            Location location = viewModel.Location;

            // Save the location to the repository
            this.locationRepository.Write(location);

            // Reload the screen.
            this.CloseTab(viewModel);
            this.OpenLocationTab(new KeyValuePair<Guid, Location>(location.Id, location));
        }

        /// <summary>Saves all currently opened locations to the repository.</summary>
        /// <param name="locationDetails">The location detail view models.</param>
        public void SaveAllLocations(ICollection<LocationDetailsViewModel> locationDetails)
        {
            foreach (LocationDetailsViewModel viewModel in locationDetails)
            {
                this.SaveLocation(viewModel);
            }
        }

        /// <summary>Deletes a location from the repository.</summary>
        /// <param name="viewModel">The location to be deleted.</param>
        public void DeleteLocation(object viewModel)
        {
            // Get the location to save
            Location location = ((LocationDetailsViewModel)viewModel).Location;

            this.locationRepository.Delete(location);
            this.Locations.Remove(location.Id);
        }

        public void CreateLocation()
        {
            throw new NotImplementedException();
        }

        public void Close()
        {
            throw new NotImplementedException();
        }
    }
}

我得到的例外情况与两个上下文菜单基本相同,这里是列表上下文菜单的详细信息:

System.Exception was unhandled
  HResult=-2146233088
  Message=No target found for method Close.
  Source=Caliburn.Micro.Platform
  StackTrace:
       bei Caliburn.Micro.ActionMessage.Invoke(Object eventArgs)
       bei System.Windows.Interactivity.TriggerBase.InvokeActions(Object parameter)
       bei System.Windows.Interactivity.EventTriggerBase.OnEvent(EventArgs eventArgs)
       bei System.Windows.Interactivity.EventTriggerBase.OnEventImpl(Object sender, EventArgs eventArgs)
       bei System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       bei System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       bei System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       bei System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
       bei System.Windows.Controls.MenuItem.InvokeClickAfterRender(Object arg)
       bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       bei System.Windows.Threading.DispatcherOperation.InvokeImpl()
       bei System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
       bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       bei System.Windows.Threading.DispatcherOperation.Invoke()
       bei System.Windows.Threading.Dispatcher.ProcessQueue()
       bei System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       bei MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       bei MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       bei System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       bei MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       bei System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       bei MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       bei MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       bei System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       bei System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
       bei System.Windows.Threading.Dispatcher.Run()
       bei System.Windows.Application.RunDispatcher(Object ignore)
       bei System.Windows.Application.RunInternal(Window window)
       bei System.Windows.Application.Run(Window window)
       bei System.Windows.Application.Run()
       bei RpgTools.Main.App.Main() in e:\Users\Robert\Documents\Visual Studio 2013\Projects\RpgTools\Tools\obj\Debug\App.g.cs:Zeile 0.
       bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       bei System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

0 个答案:

没有答案