2009-06-17 35 views
5

Thú vị vấn đề liên quan đến lệnh bắn từ mục menu ngữ cảnh ...Đi qua nguồn gốc của ContextMenu vào lệnh WPF

Tôi muốn bắn một lệnh để chèn một hàng trong kiểm soát của tôi, InsertRowCmd. Lệnh này cần biết vị trí chèn hàng.

Tôi có thể sử dụng Mouse.GetPosition(), nhưng điều đó sẽ cho tôi vị trí của con chuột hiện tại, sẽ nằm trên mục menu. Thay vào đó, tôi muốn lấy nguồn gốc của menu ngữ cảnh.

Có ai có bất kỳ đề xuất nào về cách chuyển nguồn gốc của menu ngữ cảnh làm tham số cho lệnh không?

Mẫu mã:

<UserControl x:Name="MyControl"> 
<!--...--> 
     <ContextMenu x:Name="menu"> 
      <MenuItem Header="Insert Row" Command="{x:Static customCommands:MyCommands.InsertRowCmd}" CommandParameter="?"/> 
     </ContextMenu> 
</UserControl> 

ý tưởng hiện tại của tôi như sau:

-Sử dụng handler nhấp chuột thay vì để tôi có thể tìm ra nguồn gốc trong mã. Vấn đề là sau đó tôi sẽ phải xử lý cho phép/vô hiệu hóa.

Sự kiện nhấp chuột và lưu gốc của menu ngữ cảnh. Chuyển thông tin đã lưu này vào lệnh. Tôi đã xác minh rằng các sự kiện nhấp chuột kích hoạt trước khi lệnh được thực thi.

Bất kỳ ý tưởng nào?

EDIT:

Tôi đang sử dụng Josh Smith CommandSinkBinding để định tuyến xử lý vào lớp ViewModel của tôi lệnh. Vì vậy, mã xử lý lệnh thực thi không biết gì về khung nhìn.

Trả lời

5

Bạn cần sử dụng TranslatePoint để dịch góc trên cùng bên trái (0, 0) của ContextMenu thành tọa độ trong lưới chứa. Bạn có thể làm như vậy bằng cách gắn các CommandParameter đến ContextMenu và sử dụng một bộ chuyển đổi:

CommandParameter="{Binding IsOpen, ElementName=_menu, Converter={StaticResource PointConverter}}" 

cách tiếp cận khác sẽ là một hành vi kèm theo đó tự động cập nhật một tài sản readonly gắn kiểu Point bất cứ khi nào ContextMenu được mở ra. Cách sử dụng sẽ giống như thế này:

<ContextMenu x:Name="_menu" local:TrackBehavior.TrackOpenLocation="True"> 
    <MenuItem Command="..." CommandParameter="{Binding Path=(local:TrackBehavior.OpenLocation), ElementName=_menu}"/> 
</ContextMenu> 

Vì vậy, các TrackOpenLocation tài sản gắn liền thực hiện công việc của gắn với ContextMenu và cập nhật một tài sản gắn liền thứ hai (OpenLocation) bất cứ khi nào ContextMenu được mở ra. Sau đó, MenuItem chỉ có thể liên kết với OpenLocation để có được vị trí mà tại đó ContextMenu được mở lần cuối.

+0

Tôi nghĩ bạn có nghĩa là "CommandParameter" lúc đầu không "ConverterParameter"? –

+0

Bạn có quan tâm đến việc xây dựng ý tưởng hành vi đính kèm không? –

+0

Có, cảm ơn - cố định và xây dựng. –

1

Ngoài câu trả lời của Kent, hãy suy nghĩ về "cách tiêu chuẩn". F.e. khi một ListBox có một ContextMenu, bạn không cần vị trí của menu, bởi vì mục đã chọn được thiết lập trước khi menu xuất hiện. Vì vậy, nếu kiểm soát của bạn sẽ có một cái gì đó mà được "chọn" trên nhấp chuột phải ...

+0

Hmmm ... Điều này có một số khả năng. –

+0

Tôi đã sử dụng cả hai đề xuất của bạn. Có lần khi nhấp chuột không nằm trên điều khiển con, vì vậy tôi cần đề xuất của Kent về bản dịch Điểm có hành vi được đính kèm. Khi một mục được chọn, tôi đã sử dụng mục đó thay thế. Cảm ơn! –

4

Tiếp theo từ câu trả lời của Kent, tôi đã sử dụng đề nghị tài sản gắn liền của mình và kết thúc với điều này (sử dụng Josh Smith example for attached behaviors):

public static class TrackBehavior 
{ 
public static readonly DependencyProperty TrackOpenLocationProperty = DependencyProperty.RegisterAttached("TrackOpenLocation", typeof(bool), typeof(TrackBehavior), new UIPropertyMetadata(false, OnTrackOpenLocationChanged)); 

public static bool GetTrackOpenLocation(ContextMenu item) 
{ 
    return (bool)item.GetValue(TrackOpenLocationProperty); 
} 

public static void SetTrackOpenLocation(ContextMenu item, bool value) 
{ 
    item.SetValue(TrackOpenLocationProperty, value); 
} 

public static readonly DependencyProperty OpenLocationProperty = DependencyProperty.RegisterAttached("OpenLocation", typeof(Point), typeof(TrackBehavior), new UIPropertyMetadata(new Point())); 

public static Point GetOpenLocation(ContextMenu item) 
{ 
    return (Point)item.GetValue(OpenLocationProperty); 
} 

public static void SetOpenLocation(ContextMenu item, Point value) 
{ 
    item.SetValue(OpenLocationProperty, value); 
} 

static void OnTrackOpenLocationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
{ 
    var menu = dependencyObject as ContextMenu; 
    if (menu == null) 
    { 
    return; 
    } 

    if (!(e.NewValue is bool)) 
    { 
    return; 
    } 

    if ((bool)e.NewValue) 
    { 
    menu.Opened += menu_Opened; 

    } 
    else 
    { 
    menu.Opened -= menu_Opened; 
    } 
} 

static void menu_Opened(object sender, RoutedEventArgs e) 
{ 
    if (!ReferenceEquals(sender, e.OriginalSource)) 
    { 
    return; 
    } 

    var menu = e.OriginalSource as ContextMenu; 
    if (menu != null) 
    { 
    SetOpenLocation(menu, Mouse.GetPosition(menu.PlacementTarget)); 
    } 
} 
} 

và sau đó sử dụng trong XAML, bạn chỉ cần:

<ContextMenu x:Name="menu" Common:TrackBehavior.TrackOpenLocation="True"> 
<MenuItem Command="{Binding SomeCommand}" CommandParameter="{Binding Path=(Common:TrackBehavior.OpenLocation), ElementName=menu}" Header="Menu Text"/> 
</ContextMenu> 

Tuy nhiên, tôi cũng cần phải thêm:

NameScope.SetNameScope(menu, NameScope.GetNameScope(this)); 

cho con cấu trúc của chế độ xem của tôi, nếu không, ràng buộc cho CommandParameter không thể tra cứu ElementName=menu.

+0

Tôi nghĩ rằng tôi đã có cùng một vấn đề với phạm vi tên. Cảm ơn vì bài đăng. –

+1

Một giải pháp thay thế loại bỏ nhu cầu hack NameScope là sử dụng RelativeSource binding: CommandParameter = "{Binding Path = (Phổ biến: TrackBehavior.OpenLocation), RelativeSource = {RelativeSource AncestorType = ContextMenu}}". Điều này làm việc tốt cho tôi. –

Các vấn đề liên quan