Debug data bindings in WPF using Converter

<Window x:Class="WpfTutorialSamples.DataBinding.DataBindingDebuggingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:WpfTutorialSamples.DataBinding"
        Title="DataBindingDebuggingSample" Name="wnd" Height="100" Width="200">
    <Window.Resources>
        <self:DebugDummyConverter x:Key="DebugDummyConverter" />
    </Window.Resources>
    <Grid Margin="10">
        <TextBlock Text="{Binding Title, ElementName=wnd, Converter={StaticResource DebugDummyConverter}}" />
    </Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
using System.Diagnostics;

namespace WpfTutorialSamples.DataBinding
{
    public partial class DataBindingDebuggingSample : Window
    {
        public DataBindingDebuggingSample()
        {
            InitializeComponent();
        }
    }

    public class DebugDummyConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Debugger.Break();
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Debugger.Break();
            return value;
        }
    }
}

References
https://wpf-tutorial.com/data-binding/debugging/

Handle and raising events in C#

Events

class Counter
{
    public event EventHandler ThresholdReached;

    protected virtual void OnThresholdReached(EventArgs e)
    {
        EventHandler handler = ThresholdReached;
        handler?.Invoke(this, e);
    }

    // provide remaining implementation for the class
}

Event data

public class ThresholdReachedEventArgs : EventArgs
{
    public int Threshold { get; set; }
    public DateTime TimeReached { get; set; }
}

Event handlers

class Program
{
    static void Main()
    {
        var c = new Counter();
        c.ThresholdReached += c_ThresholdReached;

        // provide remaining implementation for the class
    }

    static void c_ThresholdReached(object sender, EventArgs e)
    {
        Console.WriteLine("The threshold was reached.");
    }
}

References
https://docs.microsoft.com/en-us/dotnet/standard/events/
https://www.tutorialsteacher.com/csharp/csharp-event

Create a bindable property in WPF

public partial class FolderComponent : UserControl
{
    public static readonly DependencyProperty FolderNameProperty = DependencyProperty.Register(
        nameof(FolderName),
        typeof(string),
        typeof(FolderComponent),
        new FrameworkPropertyMetadata(
            "",
            FrameworkPropertyMetadataOptions.AffectsRender,
            new PropertyChangedCallback(OnFolderNameChanged),
            null),
        null);

    public FolderComponent()
    {
        InitializeComponent();
    }

    public string FolderName
    {
        get { return (string) GetValue(FolderNameProperty); }
        set { SetValue(FolderNameProperty, value); }
    }

    private static void OnFolderNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        FolderComponent uc = (FolderComponent) d;
        string oldValue = (string) e.OldValue;
        string newValue = (string) e.NewValue;

        uc.TextBlockFolderName.Text = newValue;
    }
}

References
https://docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/custom-dependency-properties?view=netframeworkdesktop-4.8
https://stackoverflow.com/questions/17629945/how-to-create-a-bindable-property-in-wpf
https://stackoverflow.com/questions/29763254/what-is-the-proper-method-for-creating-a-bindable-property-on-a-user-control-in

ItemsControl in WPF

A simple ItemsControl example

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlSample" Height="150" Width="200">
    <Grid Margin="10">
        <ItemsControl>
            <system:String>ItemsControl Item #1</system:String>
            <system:String>ItemsControl Item #2</system:String>
            <system:String>ItemsControl Item #3</system:String>
            <system:String>ItemsControl Item #4</system:String>
            <system:String>ItemsControl Item #5</system:String>
        </ItemsControl>
    </Grid>
</Window>

ItemsControl with data binding

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlDataBindingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsControlDataBindingSample" Height="150" Width="300">
    <Grid Margin="10">
        <ItemsControl Name="icTodoList">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Margin="0,0,0,5">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="100" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding Title}" />
                        <ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>
using System;
using System.Windows;
using System.Collections.Generic;

namespace WpfTutorialSamples.ItemsControl
{
    public partial class ItemsControlDataBindingSample : Window
    {
        public ItemsControlDataBindingSample()
        {
            InitializeComponent();

            List<TodoItem> items = new List<TodoItem>();
            items.Add(new TodoItem() { Title = "Complete this WPF tutorial", Completion = 45 });
            items.Add(new TodoItem() { Title = "Learn C#", Completion = 80 });
            items.Add(new TodoItem() { Title = "Wash the car", Completion = 0 });

            icTodoList.ItemsSource = items;
        }
    }

    public class TodoItem
    {
        public string Title { get; set; }
        public int Completion { get; set; }
    }
}

The ItemsPanelTemplate property

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlPanelSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlPanelSample" Height="150" Width="250">
    <Grid Margin="10">
        <ItemsControl>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding}" Margin="0,0,5,5" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <system:String>Item #1</system:String>
            <system:String>Item #2</system:String>
            <system:String>Item #3</system:String>
            <system:String>Item #4</system:String>
            <system:String>Item #5</system:String>
        </ItemsControl>
    </Grid>
</Window>

ItemsControl with scrollbars

<Window x:Class="WpfTutorialSamples.ItemsControl.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlSample" Height="150" Width="200">
    <Grid Margin="10">
        <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <ItemsControl>
                <system:String>ItemsControl Item #1</system:String>
                <system:String>ItemsControl Item #2</system:String>
                <system:String>ItemsControl Item #3</system:String>
                <system:String>ItemsControl Item #4</system:String>
                <system:String>ItemsControl Item #5</system:String>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</Window>

References
https://www.wpf-tutorial.com/list-controls/itemscontrol/

tunnel via multiple hops in SSH

  • I am working locally on localhost
  • host1 is accessible to localhost
  • host2 only accepts connections from host1
  • I need to create a tunnel from localhost to host2

You basically have three possibilities:

  1. Tunnel from localhost to host1:
    ssh -L 9999:host2:1234 -N host1
    

    As noted above, the connection from host1 to host2 will not be secured.

  2. Tunnel from localhost to host1 and from host1 to host2:
    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    

    This will open a tunnel from localhost to host1 and another tunnel from host1 to host2. However the port 9999 to host2:1234 can be used by anyone on host1. This may or may not be a problem.

  3. Tunnel from localhost to host1 and from localhost to host2:
    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    

    This will open a tunnel from localhost to host1 through which the SSH service on host2 can be used. Then a second tunnel is opened from localhost to host2 through the first tunnel.

Normally, I’d go with option 1. If the connection from host1 to host2 needs to be secured, go with option 2. Option 3 is mainly useful to access a service on host2 that is only reachable from host2 itself.

References
https://superuser.com/questions/96489/an-ssh-tunnel-via-multiple-hops

Bind DataContext to ViewModel in WPF

<Window x:Class="desktop.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
        xmlns:local="clr-namespace:desktop.viewmodel"
        Title="Monitoring" Height="350" Width="525" Loaded="MainWindow_OnLoaded" WindowState="Maximized">
    <Window.DataContext>
        <local:MainWindowViewModel x:Key="ViewModel" />
    </Window.DataContext>
    <DockPanel>
        <telerik:RadMenu Name="MenuMain" VerticalAlignment="Top" DockPanel.Dock="Top">
            <telerik:RadMenuItem Header="Item 1">
                <telerik:RadMenuItem Header="SubItem 1" />
                <telerik:RadMenuItem Header="SubItem 2" />
            </telerik:RadMenuItem>
            <telerik:RadMenuItem Header="Item 2" />
        </telerik:RadMenu>
        <telerik:RadBusyIndicator IsBusy="{Binding  Path=IsBusyIndicatorBusy}">
            <Frame Name="FrameMain" NavigationUIVisibility="Hidden" />
        </telerik:RadBusyIndicator>

    </DockPanel>
</Window>
public partial class MainWindow : Window
    {
        public MainWindowViewModel ViewModel { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            Init();
        }

        private void Init()
        {
            ViewModel = (MainWindowViewModel) DataContext;
        }
     }

ViewModelBase

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
    {
        if(!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

ViewModel

using System.ComponentModel;
using System.Runtime.CompilerServices;
using desktop.Annotations;
using Telerik.Windows.Controls;

namespace desktop.viewmodel
{
    public class MainWindowViewModel : ViewModelBase
    {

        private bool _isBusyIndicatorBusy = false;

        public bool IsBusyIndicatorBusy
        {
            get => _isBusyIndicatorBusy;
            set
            {
                _isBusyIndicatorBusy = value;
                SetProperty(ref _isBusyIndicatorBusy , value);
            }
        }
    }
}

References
https://stackoverflow.com/questions/25267070/setting-datacontext-of-elements-inside-xaml
https://intellitect.com/blog/getting-started-model-view-viewmodel-mvvm-pattern-using-windows-presentation-framework-wpf/

Bind DataContext to Current Window in WPF

XAML way

DataContext="{Binding RelativeSource={RelativeSource Self}}"
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1,AncestorType=UserControl}}">
<UserControl ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
</UserControl>

C# way

using System;
using System.Windows;

namespace WpfTutorialSamples.DataBinding
{
    public partial class DataContextSample : Window
    {
        public DataContextSample()
        {
            InitializeComponent();
            this.DataContext = this;
        }
    }
}

References
https://stackoverflow.com/questions/12430615/datacontext-and-binding-self-as-relativesource
https://wpf-tutorial.com/data-binding/using-the-datacontext/
https://stackoverflow.com/questions/11995318/how-do-i-bind-to-relativesource-self
https://stackoverflow.com/questions/20420001/how-to-set-datacontext-to-self