简体   繁体   中英

WPF - CommandBinding to a Command defined in a F# library doesn't work

The repo https://github.com/francotiveron/WpfApp1 contains the following projects:

  1. A C# WPF library (WpfLibrary1)
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>
using System;
using System.Windows.Input;

namespace WpfLibrary1
{
    public static class CustomCommands
    {
        public static readonly RoutedCommand DifferentAssemblyCommand = new RoutedCommand("DifferentAssemblyCommand", typeof(CustomCommands));
    }
}

  1. A F# Library (FsLibrary1)
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
      <TargetFramework>net6.0-windows</TargetFramework>
      <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Library.fs" />
  </ItemGroup>

</Project>
namespace FsLibrary1

open System.Windows.Input

type CustomCommands =
    static member FsAssemblyCommand = RoutedCommand("FsAssemblyCommand", typeof<CustomCommands>)
  1. A WPF application (WpfApp3) referencing 1 and 2.
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\FsLibrary1\FsLibrary1.fsproj" />
    <ProjectReference Include="..\WpfLibrary1\WpfLibrary1.csproj" />
  </ItemGroup>

</Project>

<Window x:Class="WpfApp3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        xmlns:lib="clr-namespace:WpfLibrary1;assembly=WpfLibrary1"
        xmlns:fs="clr-namespace:FsLibrary1;assembly=FsLibrary1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="SameAssemblyCommand" Command="local:CustomCommands.SameAssemblyCommand" HorizontalAlignment="Left" Margin="280,171,0,0" VerticalAlignment="Top" RenderTransformOrigin="-2.909,-2.677"/>
        <Button Content="DifferentAssemblyCommand" Command="lib:CustomCommands.DifferentAssemblyCommand" HorizontalAlignment="Left" Margin="280,230,0,0" VerticalAlignment="Top" RenderTransformOrigin="-2.909,-2.677"/>
        <Button Content="FsAssemblyCommand" Command="fs:CustomCommands.FsAssemblyCommand" HorizontalAlignment="Left" Margin="280,292,0,0" VerticalAlignment="Top" RenderTransformOrigin="-2.909,-2.677"/>

    </Grid>
</Window>

namespace WpfApp3
{
    public static class CustomCommands
    {
        public static readonly RoutedCommand SameAssemblyCommand = new RoutedCommand("SameAssemblyCommand", typeof(CustomCommands));
    }
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
            var cmdBindings = new CommandBinding(CustomCommands.SameAssemblyCommand);
            cmdBindings.Executed += SameAssemblyCommandExecuted;
            //cmdBindings.CanExecute += OpenChartCanExecute1;
            CommandBindings.Add(cmdBindings);
            
            cmdBindings = new CommandBinding(WpfLibrary1.CustomCommands.DifferentAssemblyCommand);
            cmdBindings.Executed += DifferentAssemblyCommandExecuted;
            //cmdBindings.CanExecute += OpenChartCanExecute1;
            CommandBindings.Add(cmdBindings);

            cmdBindings = new CommandBinding(FsLibrary1.CustomCommands.FsAssemblyCommand);
            cmdBindings.Executed += FsAssemblyCommandExecuted;
            //cmdBindings.CanExecute += OpenChartCanExecute1;
            CommandBindings.Add(cmdBindings);
        }

        private void FsAssemblyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { }
        private void SameAssemblyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { }
        private void DifferentAssemblyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { }
    }
}

The Binding to a command in the application assembly and in the C# library work as expected. Instead, the CommandBinding from the F# library doesn't work.

How can I make the F# CommandBinding work?

SOLUTION I didn't think that the F# syntax produces a property getter instead of a class field.

在此处输入图像描述

The problem is that your F# code is creating a new RoutedCommand each time FsAssemblyCommand is invoked. As a result, the corresponding command binding is never found in the element tree.

If you want this member to behave like a C# field that's only initialized once, you can use member val instead:

type CustomCommands() =
    static member val FsAssemblyCommand = RoutedCommand("OpenChartCommand", typeof<CustomCommands>)

Or you can create a let-bound value and return it each time:

type CustomCommands() =
    static let cmd = RoutedCommand("OpenChartCommand", typeof<CustomCommands>)
    static member FsAssemblyCommand = cmd

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM