-1

Im trying to draw on InkCanvas so the drawing will displays in all connected users and vice versa, but the UI does not updates with new drawings.

Im sending StrokeCollection as a MemoryStream in the StrokeCollected event to the server OnStrokeCollected() and receiving the MemoryStream from the server and creating StrokeCollection with the received MemoryStream CanvasStrokesReceived()

Here is ViewModel :

 class MainViewModel
    {
        public ObservableCollection<UserModel> Users { get; set; }
        public ObservableCollection<string> Messages { get; set; }
        public RelayCommand ConnectToServerCommand { get; set; }
        public RelayCommand SendMessageCommand { get; set; }
        public string UserName { get; set; }
        public string Message { get; set; }
        public StrokeCollection CanvasStrokes { get; set; }
        private Server _server;

        public MainViewModel()
        {
            Users = new ObservableCollection<UserModel>();
            Messages = new ObservableCollection<string>();
            CanvasStrokes = new StrokeCollection();
            _server = new Server();
            _server.connectedEvent += UserConnected;
            _server.msgReceivedEvent += MessageReceived;
            _server.inkStrokeReceivedEvent += CanvasStrokesReceived;
            _server.userDisconnectEvent += RemoveUser;

            ConnectToServerCommand = new RelayCommand(o => _server.ConnectToServer(UserName), o => !string.IsNullOrEmpty(UserName));
            SendMessageCommand = new RelayCommand(o => _server.SendMessageToServer(Message), o => !string.IsNullOrEmpty(Message));
        }


        public void OnStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
        {
            MemoryStream _ms = new MemoryStream();
            CanvasStrokes.Save(_ms);
            _ms.Flush();

            _server.SendCanvasStrokesToServer(_ms);
        }


        private void CanvasStrokesReceived()
        {
            Application.Current.Dispatcher.Invoke(() =>
            {
                var strokes = _server.PacketReader.ReadCanvasStrokes();
                CanvasStrokes = new StrokeCollection(strokes);
            });
        }




        private void UserConnected()
        {
            var user = new UserModel
            {
                UserName = _server.PacketReader.ReadMessage(),
                UID = _server.PacketReader.ReadMessage()
            };
            if (!Users.Any(x => x.UID == user.UID))
            {
                Application.Current.Dispatcher.Invoke(() => Users.Add(user));
            }

        }

        private void MessageReceived()
        {
            var msg = _server.PacketReader.ReadMessage();
            Application.Current.Dispatcher.Invoke(() => Messages.Add(msg));

        }
        private void RemoveUser()
        {
            var uid = _server.PacketReader.ReadMessage();
            var user = Users.Where(u => u.UID == uid).FirstOrDefault();
            Application.Current.Dispatcher.Invoke(() => Users.Remove(user));
        }

    }

Here is my View :

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var mvm = new MainViewModel();
            this.DataContext = mvm;
            inkCanv.StrokeCollected += mvm.OnStrokeCollected;
            inkCanv.Strokes = mvm.CanvasStrokes;
        }
    }

Here is my XAML :

<InkCanvas          
    x:Name="inkCanv" 
    Grid.Column="1" 
    Grid.Row="1" 
    Height="130" 
    Background="Azure"
    >
</InkCanvas>
Kosta
  • 9
  • 2
  • Note that MemoryStream implements IDisposable (and IDisposableAsync). You should define _ms in the scope of a using-block to close and dispose the stream automatically. – BionicCode Nov 09 '21 at 19:59
  • Also, instead of enqueuing the collection changes to the Dispatcher consider to call [`BindingOperations.EnableCollectionSynchronization`](https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.bindingoperations.enablecollectionsynchronization?view=windowsdesktop-5.0) (from the constructor of MainViewModel for each collection). – BionicCode Nov 09 '21 at 20:03
  • Changing proprties of MainViewModel that serve as a binding source like CanvasStrokes must raise the INotifyPropertyChanged.PropertyChanged event. – BionicCode Nov 09 '21 at 21:09

1 Answers1

0

It looks like you are missing to update the canvas. You have to do this explicitly or using data binding (recommended):

<InkCanvas Strokes="{Binding CanvasStrokes}" />

Then in the constructor of MainWindow remove the assignment:

public MainWindow()
{
  InitializeComponent();
  var mvm = new MainViewModel();
  this.DataContext = mvm;
  inkCanv.StrokeCollected += mvm.OnStrokeCollected;
  // inkCanv.Strokes = mvm.CanvasStrokes;
}
BionicCode
  • 1
  • 4
  • 28
  • 44