0

I am trying to understand how a wpf custom control could be written in F#.

As an example, I have the following C# code for a drag and drop on a canvas (in C#). It inherits from ListBox. I'm not looking for anybody to rewrite this. But I'm at a loss as to how it would be implemented in Elmish.wpf since there is no xaml to deal with. (I believe a Custom Control does not have a XAML interface).

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Stargate.XI.Client.Views.CustomControls
{
    public delegate void DropCompletedEventHandler(object sender, DropCompletedEventArgs e);


    // To add a custom DropCompletedEvent to an ItemsControl, I would either have to have an attached property, as in
    // https://stackoverflow.com/questions/15134514/attached-behavior-handling-an-attached-event-in-wpf 
    // or subclass an ItemsControl as below. Creating a simple custom control, like here, seems cleaner.
    // Note: ItemsControl can't select items, only present collections. Only a Selector or one of it's descendants can select items
    //       Hence, only the ListBox or its derivative,ListView, have Selector's.
    public class ChartCanvas : ListBox
    {
        public event EventHandler PlayMusicEvent;
        public event EventHandler PauseMusicEvent;
        public event EventHandler StopMusicEvent;
        public event EventHandler DisposeMusicEvent;
        public event EventHandler DisposePosterEvent;

        #region DropCompletedEvent
        // Create a custom routed event by first registering a RoutedEventID
        // This event uses the bubbling routing strategy
        public static readonly RoutedEvent DropCompletedEvent = EventManager.RegisterRoutedEvent(
            "DropCompleted", RoutingStrategy.Bubble, typeof(DropCompletedEventHandler), typeof(ChartCanvas));

        // Provide CLR accessors for the event. The RoutedEventHandler, e.g., "DropCompleted" is used in the xaml declaration for the ImageCanvas.
        public event DropCompletedEventHandler DropCompleted
        {
            add { AddHandler(DropCompletedEvent, value); }
            remove { RemoveHandler(DropCompletedEvent, value); }
        }

        // This method raises the DropCompleted event
        public void RaiseDropCompletedEvent(object datatype)
        {
            RaiseEvent(new DropCompletedEventArgs(DropCompletedEvent, datatype));
        }
        #endregion

        public ChartCanvas()
        {
            AllowDrop = true;
            DragEnter += IC_DragEnter;
            Drop += IC_Drop;
            DragOver += IC_DragOver;
            DragLeave += IC_DragLeave;
        }

        private void IC_DragLeave(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }

        private void IC_DragOver(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }

        private void IC_Drop(object sender, DragEventArgs e)
        {
            var data = e.Data.GetData(DataFormats.Text);
            var dragSource = e.Data.GetData("DragSource");

            RaiseDropCompletedEvent(data);
        }

        private void IC_DragEnter(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }

        #region PlayMovie
        private ICommand _playMovie;
        public ICommand PlayMovieCommand
        {
            get
            {
                if (_playMovie == null)
                {
                    _playMovie = new RelayCommand(
                        p => true,
                        p => this.PlayMovie());
                }
                return _playMovie;
            }
        }

        private void PlayMovie()
        {
            PlayMusicEvent?.Invoke(this, EventArgs.Empty);
        }
        #endregion

        #region PauseMovie
        private ICommand _pauseMovie;
        public ICommand PauseMovieCommand
        {
            get
            {
                if (_pauseMovie == null)
                {
                    _pauseMovie = new RelayCommand(
                        p => true,
                        p => this.PauseMovie());
                }
                return _pauseMovie;
            }
        }

        private void PauseMovie()
        {
            PauseMusicEvent?.Invoke(this, EventArgs.Empty);
        }
        #endregion

        #region StopMovie
        private ICommand _stopMovie;
        public ICommand StopMovieCommand
        {
            get
            {
                if (_stopMovie == null)
                {
                    _stopMovie = new RelayCommand(
                        p => true,
                        p => this.StopMovie());
                }
                return _stopMovie;
            }
        }

        private void StopMovie()
        {
            StopMusicEvent?.Invoke(this, EventArgs.Empty);
        }
        #endregion

        public bool Dispose
        {
            get { return (bool)GetValue(DisposeProperty); }
            set { SetValue(DisposeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Dispose.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DisposeProperty =
            DependencyProperty.Register("Dispose", typeof(bool), typeof(ChartCanvas), new PropertyMetadata(false,
                (s,e) =>
                {
                    ChartCanvas chartcanvas = s as ChartCanvas;
                    chartcanvas.DisposeMusicEvent?.Invoke(chartcanvas, EventArgs.Empty);
                    chartcanvas.DisposePosterEvent?.Invoke(chartcanvas, EventArgs.Empty);
                }              
                ));

    }
}

Any suggestions to this newbie as to how to approach this would be much appreciated.

TIA

Alan Wayne
  • 5,122
  • 10
  • 52
  • 95
  • 1
    Where did you get the idea that Elmish.WPF can be used to write custom controls? I don't think that makes sense, though I might be wrong. Also, I have never tried to write a WPF custom control, but I suspect I'd use C# for that kind of challenge, with maybe a tad F# for some behind-the-scenes business logic. F# is good for 99.9% of what I need to code, but perhaps not this. – Bent Tranberg May 06 '22 at 19:27
  • @BentTranberg I have seen where F# (Elmish.wpf) can be used to write adorners. I was just wondering how it would work for a custom control, although I would have to admit, writing it in C# does seem fairly easy. – Alan Wayne May 06 '22 at 20:08
  • It's quite possible to write a custom control in F#. I'm not quite sure I understand the question, though. – Jim Foye May 06 '22 at 20:39
  • @JimFoye I guess my biggest problem would be how would one integrate a custom control built in F# with a xaml of wpf that would implement a routed event? ANY example would be extremely helpful. Thanks. – Alan Wayne May 06 '22 at 21:24
  • 1
    You want to use the ChartCanvas you have defined, as is, from an Elmish application? If it's already complete, I'm not sure it's worth rewriting it in F#. You should just be able to use it from a C# assembly (so, a separate project, either your main C# UI assembly or another one just for this control - as opposed to your F# project). But if you do want to write it in F#, then create an F# library, reference the 3 main WPF assemblies, write it in F#, then reference that project where needed (eitther C# or F#). I did this with a custom panel recently. – Jim Foye May 06 '22 at 21:41
  • @JimFoye I will try...would it be possible to share that panel? Thanks. – Alan Wayne May 06 '22 at 21:48
  • @JimFoye Your suggestion is perfect -- and saved me many hours of work. It should be the answer to my question. Thanks. – Alan Wayne May 07 '22 at 14:48

0 Answers0