Cal.Message.Attach - 找不到方法的目标

Cal.Message.Attach - No target found for method

我用 Caliburn.Micro 在 WPF 中设计了一个界面。我已经自定义了单选按钮,这些单选按钮在选中时由 cal:Message.Attach 属性 触发。但是当我检查其中一个具有此 属性 的单选按钮时,我得到以下错误:No target found for method

我的FarKornaView.xaml:

<Grid 
    Style="{StaticResource ContentRoot}"
    x:Class="IKA.Views.FarKornaView"
    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:mui="http://firstfloorsoftware.com/ModernUI"
    xmlns:cal="http://www.caliburnproject.org"
    xmlns:model="clr-namespace:IKA.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:ika="clr-namespace:IKA"
    mc:Ignorable="d"
    d:DataContext="{x:Type model:FarKornaViewModel}">
<Grid.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="../Resources/toggle.xaml"></ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
        <ControlTemplate x:Key="RadioButtonStyle" TargetType="ika:RadioButtonExtension">
            <ika:RadioButtonExtension Style="{StaticResource AnimatedSwitch}"  Width="100" Height="40" HorizontalAlignment="Left" Content="{TemplateBinding ContentPresenter.Content}"   GroupName="{TemplateBinding GroupName}" IsChecked="{TemplateBinding IsChecked}" />
        </ControlTemplate>
    </ResourceDictionary>
</Grid.Resources>
<Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"></ColumnDefinition>
    <ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Sol Far"  Style="{StaticResource Heading2}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="Sağ Far"  Style="{StaticResource Heading2}" />
<TextBlock Grid.Column="0" Grid.Row="3" Text="Toros Farı" Style="{StaticResource Heading2}" />
<TextBlock Grid.Column="1" Grid.Row="3" Text="Angel Eyes"  Style="{StaticResource Heading2}"/>

<ika:RadioButtonExtension x:Name="far_1" Grid.Column="0" Grid.Row="1" GroupName="Sol" Template="{StaticResource RadioButtonStyle}"  Content="Sürekli Yak" />
<ika:RadioButtonExtension x:Name="far_2" Grid.Column="0" Grid.Row="2" GroupName="Sol" Template="{StaticResource RadioButtonStyle}"  Content="Yak Söndür"/>
<ika:RadioButtonExtension x:Name="far_3" Grid.Column="1" Grid.Row="1" GroupName="Sağ" Template="{StaticResource RadioButtonStyle}"  Content="Sürekli Yak"  />
<ika:RadioButtonExtension x:Name="far_4" Grid.Column="1" Grid.Row="2" GroupName="Sağ" Template="{StaticResource RadioButtonStyle}"  Content="Yak Söndür"   />
<ika:RadioButtonExtension x:Name="far_5" Grid.Column="0" Grid.Row="4" GroupName="Toros" Template="{StaticResource RadioButtonStyle}" Content="Sürekli Yak"  cal:Message.Attach="RadioButtonHandler(far_5.GroupName,far_5.Content,far_5.IsChecked)"/>
<ika:RadioButtonExtension x:Name="far_6" Grid.Column="0" Grid.Row="5" GroupName="Toros" Template="{StaticResource RadioButtonStyle}" Content="Yak Söndür"   cal:Message.Attach="RadioButtonHandler(far_6.GroupName,far_6.Content,far_6.IsChecked)"/>
<ika:RadioButtonExtension x:Name="far_7" Grid.Column="1" Grid.Row="4" GroupName="Angel" Template="{StaticResource RadioButtonStyle}" Content="Sürekli Yak"  cal:Message.Attach="RadioButtonHandler(far_7.GroupName,far_7.Content,far_7.IsChecked)"/>
<ika:RadioButtonExtension x:Name="far_8" Grid.Column="1" Grid.Row="5" GroupName="Angel" Template="{StaticResource RadioButtonStyle}" Content="Yak Söndür"   cal:Message.Attach="RadioButtonHandler(far_8.GroupName,far_8.Content,far_8.IsChecked)"/>
</Grid>

我将单选按钮样式设置为切换按钮的代码,如 iPhone 样式:http://marcangers.com/animated-switch-togglebutton-style-in-wpf/

    <ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:cal="http://www.caliburnproject.org"
  xmlns:ika="clr-namespace:IKA">
    <Style x:Key="AnimatedSwitch" TargetType="{x:Type ika:RadioButtonExtension}">
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="Background" Value="#FAFAFB" />
        <Setter Property="BorderBrush" Value="#EAEAEB" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ika:RadioButtonExtension">
                    <Viewbox Stretch="Uniform">
                        <Canvas Name="Layer_1" Width="100" Height="20" Canvas.Left="10" Canvas.Top="0">
                            <ContentPresenter Canvas.Left="0" Width="70" Height="20" HorizontalAlignment="Left"/>
                            <!--<ContentPresenter Canvas.Left="0" Width="20" Height="20" ContentSource="Content" VerticalAlignment="Center" RecognizesAccessKey="True" />-->
                            <Ellipse  Canvas.Left="80" Width="20" Height="20" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.9"/>
                            <Ellipse  Canvas.Left="95" Width="20" Height="20" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.9"/>
                            <Border   Canvas.Left="90" Width="15" Height="20" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0.51,0,0.51" />
                            <Ellipse x:Name="ellipse"  Canvas.Left="80" Width="20" Height="20" Fill="White" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.3">
                                <Ellipse.RenderTransform>
                                    <TranslateTransform X="0" Y="0" />
                                </Ellipse.RenderTransform>
                                <Ellipse.BitmapEffect>
                                    <DropShadowBitmapEffect Softness="0.1" ShadowDepth="0.7" Direction="270" Color="#BBBBBB"/>
                                </Ellipse.BitmapEffect>
                            </Ellipse>
                        </Canvas>
                    </Viewbox>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="True" >
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetProperty="Background.Color" To="#52D468" Duration="0:0:0.2" />
                                        <ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="#41C955" Duration="0:0:0.2" />
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" Storyboard.TargetName="ellipse">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="15" KeySpline="0, 1, 0.6, 1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetProperty="Background.Color" To="#FAFAFB" Duration="0:0:0.2" />
                                        <ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="#EAEAEB" Duration="0:0:0.2" />
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" Storyboard.TargetName="ellipse">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="15"/>
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.3" Value="0" KeySpline="0, 0.5, 0.5, 1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

还有我的 RadioButtonExtension class,它使单选按钮无法选中,并获得了 classic Radiobutton 组的功能,这些样式的单选按钮:

      public class RadioButtonExtension : RadioButton
  {
    public static int WasChecked { get; set; }

    public bool? IsCheckedChanged
    {
        get
        {
            return (bool?)GetValue(IsCheckedChangedProperty);
        }
        set
        {
            SetValue(IsCheckedChangedProperty, value);
        }
    }

    // Using a DependencyProperty as the backing store for IsChanged.  
   //This enables animation, styling, binding, etc...
  public static readonly DependencyProperty IsCheckedChangedProperty =
        DependencyProperty.Register("IsChanged", typeof(bool?), 
          typeof(RadioButtonExtension),
      new FrameworkPropertyMetadata(false, 
      FrameworkPropertyMetadataOptions.Journal |
      FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        CheckedChanged));

     public static void CheckedChanged(DependencyObject d, 
              DependencyPropertyChangedEventArgs e)
     {
       ((RadioButtonExtension)d).IsChecked = (bool)e.NewValue;
     }

     public RadioButtonExtension()
     {
        this.Click += RadioButtonExtension_Click;
        this.Checked += new RoutedEventHandler(RadioButtonExtension_Checked); 
     }

     void RadioButtonExtension_Click(object sender, System.Windows.RoutedEventArgs e)
     {
         if (WasChecked > 0 && IsCheckedChanged==true)
         {
             this.IsChecked = false;
             WasChecked = 0;

         }
         else if (WasChecked == 0 && IsCheckedChanged == true)
         {
             WasChecked = 1;
         }
     }

    void RadioButtonExtension_Checked(object sender, System.Windows.RoutedEventArgs e)
    {
        if (this.IsChecked == true)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(this);
            DependencyObject parent2 = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(parent));
            var a = parent2.Ancestors().OfType<RadioButtonExtension>()
                .OrderBy(x => x.GroupName)
                .Where(x => x.GroupName == this.GroupName && x.Name != this.Name)
                .AsEnumerable();
            foreach (RadioButtonExtension button in a)
            {
                button.IsChecked = false;
            }
            IsCheckedChanged = true;
            WasChecked = 0;
        }
        else
        {
            IsCheckedChanged = false;
        }
    }
  }

最后我的 FarKornaViewModel 包含 RadioButtonHandler 方法。

 public sealed class FarKornaViewModel : Screen, IMainScreenTabItem
{
    private FarKontrol _farKontrol;
    public FarKornaViewModel(FarKontrol farKontrol)
    {
        _farKontrol = farKontrol;
        DisplayName = "Far Kontrol";
    }
    public void RadioButtonHandler(string groupname, string content, bool isChecked)
    {
        FarDeger.FarIsim = groupname;
        FarDeger.Secim = content;
        FarDeger.isChecked = isChecked;
        _farKontrol.SendCommand();
    }
}

MainView.xaml:

<mui:ModernWindow x:Class="IKA.Views.MainView"
    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:mui="http://firstfloorsoftware.com/ModernUI"
    xmlns:model="clr-namespace:IKA.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"
    d:DataContext="{x:Type model:MainViewModel}"
    Title="{Binding WindowTitle}"
    Height="400" Width="700">
<mui:ModernWindow.MenuLinkGroups>
    <mui:LinkGroup DisplayName="Kontrol" >
        <mui:LinkGroup.Links>
            <mui:Link DisplayName="Far ve Korna Kontrol" Source="..\Views\FarKornaView.xaml"/>
            <mui:Link DisplayName="Bağlantı" Source="..\Views\ConnectionView.xaml"/>
        </mui:LinkGroup.Links>
    </mui:LinkGroup>
    <mui:LinkGroup DisplayName="İKA" >
        <mui:LinkGroup.Links>
            <mui:Link DisplayName="Hakkında" />
            <mui:Link DisplayName="Sistem" />
            </mui:LinkGroup.Links>
        </mui:LinkGroup>
</mui:ModernWindow.MenuLinkGroups>

MainViewModel.cs :

    public class MainViewModel : Screen
{
    private const string WindowTitleDefault = "IKA - Control Panel";
    private string _windowTitle = WindowTitleDefault;
    public MainViewModel()
    {
    }
    public string WindowTitle
    {
        get { return _windowTitle; }
        set
        {
            _windowTitle = value;
            NotifyOfPropertyChange(() => WindowTitle);
        }
    }

编辑 1: 我从 RadioButtonExtensions 检查了单选按钮的 DataContext,我注意到它似乎是 MainViewModel,而不是 FarKornaViewModel,但我不明白为什么。

编辑 2: 即使我没有设置数据上下文,它也会自动设置 MainViewModel。有些东西可能将 MainViewModel 设置为默认 VM,但我不知道这是因为 Caliburn 或 Modern.UI 或其他原因。

编辑 3: 我发现FarKornaView 运行首先是因为MainView中的MenuLinkGroups所以FarKornaViewModel不能运行,FarKornaView必须在MainView中显示。我想我应该触发 ViewModel 而不是从链接查看。

指定 cal:Action.TargetWithoutContext="..." 以及 cal:Message.Attach 并确保将其绑定到 FarKornaViewModel。发生的事情是 Caliburn 在附加目标上使用当前数据上下文(这是错误的视图模型)。如果 MainViewModel 是数据上下文,则可以向其添加 属性 以公开您的 FarKornaViewModel,然后您的目标声明将是 cal:Action.TargetWithoutContext="{Binding FarKornaViewModel}"。

我终于解决了这个问题。我创建了一个名为 "ViewModelsContainer" 的静态 class :

    public static class ViewModelsContainer
{
    public static FarKornaViewModel FarKornaViewModel { get; set; }
}

然后在 MainViewModel 的构造函数中,将 FarKornaViewModel 的实例设置为 FarKornaViewModel 属性。

 public MainViewModel(FarKornaViewModel farKornaViewModel)
        {
            ViewModelsContainer.FarKornaViewModel = farKornaViewModel;
        }

最后在我的 FarKornaView.xaml.cs 中,我将此 属性 设置为 DataContext。

public FarKornaView()
{
    InitializeComponent();
    this.DataContext = ViewModelsContainer.FarKornaViewModel;
}