Unable to set property in usercontrol from viewmodel - c#

I am trying to set a property in a usercontrol from a view model, the control is meant to capture and display the users signature.
I am having problems setting the property in the usercontrol from a view model.
Receiving the signature from the usercontrol into the view model works fine.
Any ideas on how to solve this would be of greatly appreciated.
My test code is below.
User control XAML:
<UserControl x:Class="mvvmSignature.ucSignature"
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:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="212" d:DesignWidth="300" Background="#FFEFFCEF">
<Grid Height="210">
<InkCanvas MinHeight="73" HorizontalAlignment="Left" Margin="10,27,0,0" Name="inkCanvas1" VerticalAlignment="Top" MinWidth="278" LostMouseCapture="inkCanvas1_LostMouseCapture" />
<Button Content="Clear" Height="23" HorizontalAlignment="Left" Margin="33,142,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="buttonClear_Click" />
<Button Content="Load on user control" Height="23" Margin="143,143,36,45" Click="ButtonLoad_Click" />
</Grid>
</UserControl>
UserControl CodeBehind:
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
namespace mvvmSignature
{
public partial class ucSignature : UserControl
{
public ucSignature()
{
InitializeComponent();
}
private void inkCanvas1_LostMouseCapture(object sender, MouseEventArgs e)
{
SetSignature();
}
private void SetSignature()
{
var sb = new StringBuilder();
using (var ms = new MemoryStream())
{
inkCanvas1.Strokes.Save(ms);
foreach (var item in ms.ToArray())
{
sb.AppendFormat("{0},",
item);
}
ms.Close();
}
var local = sb.ToString().Trim() + "¬¬¬";
local = local.Replace(",¬¬¬", string.Empty);
Signature = local;
}
private void LoadSignature(string signatureIn)
{
if (string.IsNullOrEmpty(signatureIn) || !signatureIn.Contains(",")) return;
var x = signatureIn.Split(',').Select(byte.Parse).ToArray();
if (!x.Any()) return;
using (var ms = new MemoryStream(x))
{
inkCanvas1.Strokes = new StrokeCollection(ms);
ms.Close();
}
}
public static DependencyProperty SignatureProperty =
DependencyProperty.Register("Signature", typeof(string),
typeof(ucSignature));
public string Signature
{
get { return (string) GetValue(SignatureProperty); }
set
{
SetValue(SignatureProperty, value);
LoadSignature(value);
}
}
private void ButtonLoad_Click(object sender, RoutedEventArgs e)
{
Signature = "0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144";
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
inkCanvas1.Strokes.Clear();
SetSignature();
}
}
}
MainWindow XAML:
<Window xmlns:my="clr-namespace:mvvmSignature" x:Class="mvvmSignature.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:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="490"
Width="327"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<StackPanel>
<my:ucSignature Signature ="{Binding Signature,Mode=TwoWay}" />
<Button Width ="140" Height ="30" Content ="Load On Main Page" Command="{Binding LoadOnMainPageCommand}" />
<TextBox FontSize="8"
Foreground="Purple"
Text="{Binding Signature,Mode=TwoWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" Height="210" />
</StackPanel>
MainViewModel:
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace mvvmSignature.ViewModel
{
public class MainViewModel : ViewModelBase
{
public ICommand LoadOnMainPageCommand { get; set; }
public const string WelcomeTitlePropertyName = "Signature";
private string _signature = string.Empty;
public string Signature
{
get
{
return _signature;
}
set
{
if (_signature == value)
{
return;
}
_signature = value;
RaisePropertyChanged(WelcomeTitlePropertyName);
}
}
public MainViewModel()
{
LoadOnMainPageCommand = new RelayCommand(LoadSignature);
}
private void LoadSignature()
{
Signature ="0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144";
}
}
}

I re-wrote portions of your ucSignature class to properly declare and use the Signature property in your code behind. Use this class definition instead, and let me know if it works any better.
public partial class ucSignature : UserControl
{
public ucSignature()
{
InitializeComponent();
}
private void inkCanvas1_LostMouseCapture(object sender, MouseEventArgs e)
{
var sb = new StringBuilder();
using (var ms = new MemoryStream())
{
inkCanvas1.Strokes.Save(ms);
foreach (var item in ms.ToArray())
{
sb.AppendFormat("{0},", item);
}
ms.Close();
}
var local = sb.ToString().Trim() + "¬¬¬";
local = local.Replace(",¬¬¬", string.Empty);
this.SetValue(SignatureProperty, local);
}
private void LoadSignature(string signatureIn)
{
if (string.IsNullOrEmpty(signatureIn) || !signatureIn.Contains(",")) return;
var x = signatureIn.Split(',').Select(byte.Parse).ToArray();
if (!x.Any()) return;
using (var ms = new MemoryStream(x))
{
inkCanvas1.Strokes = new StrokeCollection(ms);
ms.Close();
}
}
public static DependencyProperty SignatureProperty = DependencyProperty.Register(
"Signature",
typeof(string),
typeof(ucSignature),
new UIPropertyMetadata(OnSignaturePropertyChanged));
public static string GetSignature(ucSignature signature)
{
return (string)signature.GetValue(SignatureProperty);
}
public static void SetSignature(ucSignature signature, string value)
{
signature.SetValue(SignatureProperty, value);
}
private static void OnSignaturePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var signature = obj as ucSignature;
if (signature != null)
{
LoadSignature(args.NewValue.ToString());
}
}
private void ButtonLoad_Click(object sender, RoutedEventArgs e)
{
this.SetValue(SignatureProperty, "0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144");
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
inkCanvas1.Strokes.Clear();
this.SetValue(SignatureProperty, string.Empty);
}
}

You need to set your MainViewModel as the DataContext of your MainWindow so the binding works. Unless you're setting it in the constructor, I don't see it being set anywhere. You can try something like this:
<Application.Resources>
<local:MainViewModel x:Key="MainViewModel" />
</Application.Resources>
DataContext="{StaticResource MainViewModel}"

I'm going to assume that it's the Signature property that you are trying to set, mainly because that's what your user control is all about AND that's where I see an issue.
After you set the Signature property, you call RaisePropertyChanged on a different property: the WelcomeTitlePropertyName property. You should be calling RaisePropertyChanged on the same property you set or else your UI (i.e. your user control) won't know about it.
For example, in your MainWindow XAML you placed a ucSignature element like this:
<my:ucSignature Signature ="{Binding Signature,Mode=TwoWay}" />
That part is fine, but in your ViewModel that property should be defined like this:
private string _signature = string.Empty;
public string Signature
{
get { return _signature; }
set
{
if (_signature == value)
return;
_signature = value;
RaisePropertyChanged(Signature);
}
}

Related

UserControl in WinUI 3: how to set the 'source' property of an image and the 'click' event of a button?

I'm building a UserControl that should display a button with an image and text.
I access that UserControl in the App like this:
<local:ButtonWithImage
ButtonClick="Button1_Click"
ButtonImage="Assets/Clipboard 4.png"
ButtonText="Clipboard History"
ButtonWidth="200" />
Out of the 4 properties displayed in the code above, two of them are working fine, which are ButtonText and ButtonWidth.
But the ButtonClick and ButtonImage properties are causing errors, which I'll explain next.
The UserControl code is this:
xaml:
<UserControl
x:Class="Launcher_WinUI3_Net_6.ButtonWithImage"
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:local="using:Launcher_WinUI3_Net_6"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="button">
<StackPanel Orientation="Horizontal">
<Image x:Name="image"/>
<TextBlock x:Name="textBlock" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock Height="1" />
</StackPanel>
</UserControl>
C#:
public sealed partial class ButtonWithImage : UserControl
{
public ButtonWithImage()
{
this.InitializeComponent();
}
public string ButtonText
{
get { return (string)GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty
ButtonTextProperty =
DependencyProperty.Register("ButtonText",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonTextValue));
private static void ButtonTextValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("textBlock") as TextBlock;
buttonWithImageProperty.Text = e.NewValue.ToString();
}
public string ButtonWidth
{
get { return (string)GetValue(ButtonWidthProperty); }
set { SetValue(ButtonWidthProperty, value); }
}
public static readonly DependencyProperty
ButtonWidthProperty =
DependencyProperty.Register("ButtonWidth",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonWidthValue));
private static void ButtonWidthValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("button") as Button;
buttonWithImageProperty.Width = Convert.ToDouble(e.NewValue.ToString());
}
public string ButtonClick
{
get { return (string)GetValue(ButtonClickProperty); }
set { SetValue(ButtonClickProperty, value); }
}
public static readonly DependencyProperty
ButtonClickProperty =
DependencyProperty.Register("ButtonClick",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonClickValue));
private static void ButtonClickValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("button") as Button;
buttonWithImageProperty.Click += e.NewValue.ToString();
}
public string ButtonImage
{
get { return (string)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
public static readonly DependencyProperty
ButtonImageProperty =
DependencyProperty.Register("ButtonImage",
typeof(string), typeof(ButtonWithImage),
new PropertyMetadata(string.Empty, ButtonImageValue));
private static void ButtonImageValue(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var buttonWithImage = d as ButtonWithImage;
var buttonWithImageProperty = buttonWithImage.FindName("image") as Image;
buttonWithImageProperty.Source = e.NewValue.ToString();
}
}
The code for the ButtonClick is generating this error: Cannot implicitly convert type 'string' to 'Microsoft.UI.Xaml.RoutedEventHandler'
And the code for the ButtonImage is generating this error: Cannot implicitly convert type 'string' to 'Microsoft.UI.Xaml.Media.ImageSource'
I don't have much experience with creating UserControls so I'm following some examples I've seen on the internet, but none of them address these two problems I'm facing.
========================================================
Update 1 based on answer from Andrew KeepCoding:
Thanks Andrew!!!
There is still an error going on: No overload for 'Button52_Click' matches delegate 'EventHandler'
UserControl in the App:
<local:ButtonWithImage
ButtonImage="Assets/Clipboard 4.png"
ButtonText="Clipboard History"
ButtonWidth="200"
Click="Button52_Click" />
Button52_Click signature:
private void Button52_Click(object sender, RoutedEventArgs e)
{
foo();
}
UserControl 'Click' event signature:
public event EventHandler? Click;
private void button_Click(object sender, RoutedEventArgs e)
{
Click?.Invoke(this, EventArgs.Empty);
}
The signatures are the same, even so the error No overload for 'Button52_Click' matches delegate 'EventHandler' is occurring
The error is occurring here, in 'case 41:':
case 40: // MainWindow.xaml line 1288
{
global::Microsoft.UI.Xaml.Controls.Button element40 = global::WinRT.CastExtensions.As<global::Microsoft.UI.Xaml.Controls.Button>(target);
((global::Microsoft.UI.Xaml.Controls.Button)element40).Click += this.Button51_Click;
}
break;
case 41: // MainWindow.xaml line 1199
{
global::Launcher_WinUI3_Net_6.ButtonWithImage element41 = global::WinRT.CastExtensions.As<global::Launcher_WinUI3_Net_6.ButtonWithImage>(target);
((global::Launcher_WinUI3_Net_6.ButtonWithImage)element41).Click += this.Button52_Click;
}
break;
========================================================
Update 2:
The Button52_Click signature should be:
private void Button52_Click(object sender, EventArgs e)
{
foo();
}
And not:
private void Button52_Click(object sender, RoutedEventArgs e)
{
foo();
}
Instead of typeof(string), you should use the actual type for your dependency properties.
For example, I'm using ImageSource for the ButtonImage in the code below:
<UserControl
x:Class="UserControls.ButtonWithImage"
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"
mc:Ignorable="d">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button
x:Name="button"
Click="button_Click">
<StackPanel Orientation="Horizontal">
<Image
x:Name="image"
Source="{x:Bind ButtonImage, Mode=OneWay}" />
<TextBlock
x:Name="textBlock"
Text="{x:Bind ButtonText, Mode=OneWay}" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock Height="1" />
</StackPanel>
</UserControl>
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
namespace UserControls;
public sealed partial class ButtonWithImage : UserControl
{
public static readonly DependencyProperty ButtonTextProperty = DependencyProperty.Register(
nameof(ButtonText),
typeof(string),
typeof(ButtonWithImage),
new PropertyMetadata(default));
public static readonly DependencyProperty ButtonImageProperty = DependencyProperty.Register(
nameof(ButtonImage),
typeof(ImageSource),
typeof(ButtonWithImage),
new PropertyMetadata(default));
public ButtonWithImage()
{
this.InitializeComponent();
}
public event EventHandler? Click;
public string ButtonText
{
get => (string)GetValue(ButtonTextProperty);
set => SetValue(ButtonTextProperty, value);
}
public ImageSource? ButtonImage
{
get => (ImageSource?)GetValue(ButtonImageProperty);
set => SetValue(ButtonImageProperty, value);
}
private void button_Click(object sender, RoutedEventArgs e)
{
Click?.Invoke(this, EventArgs.Empty);
}
}
And use it like this:
<local:ButtonWithImage
ButtonText="Text"
ButtonImage="Assets/StoreLogo.png"
Click="ButtonWithImage_Click" />
private void ButtonWithImage_Click(object sender, EventArgs e)
{
}
You should also consider a custom control derived from a Button. These videos might help.
UserControls
CustomControls

WPF how to access a Class Values from another form?

Goal:
Using WPF I have created a Class called User.cs
here it is:
class User
{
public int id;
public int access;
public string username;
public int ID
{
get { return id; }
set { id = value; }
}
public int Access
{
get { return access; }
set { access = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
At my MainWindow.xaml.cs
I create a user and assign a value.
User u = new User();
private void Button_Click(object sender, RoutedEventArgs e)
{
u.id = 1;
u.access = 2;
u.username = "User1";
}
Question
From my new xaml form called Dashboard.xaml.cs. How can I access the information saved from MainWindow.xaml.cs ?
What I have tried
At Dashboard.xaml.cs
private void Window_Loaded(object sender, RoutedEventArgs e)
{
txt.Content = User.username
}
UPDATE: Using MVVM:
After some research and copying some examples here is where I got to.
Project Tree:
Ignore LoginScreen it is not used at all.
UserModel.cs
using System.ComponentModel;
namespace Technical_Application.Model
{
public class UserModel { }
public class User : INotifyPropertyChanged
{
private int id;
private int accessID;
private string username;
public int Id
{
get
{
return id;
}
set
{
if(id != value)
{
id = value;
RaisePropertyChanged("Id");
}
}
}
public int AccessID
{
get
{
return accessID;
}
set
{
if (accessID != value)
{
accessID = value;
RaisePropertyChanged("AccessID");
}
}
}
public string Username
{
get
{
return username;
}
set
{
if( username!= value)
{
username = value;
RaisePropertyChanged("Username");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
UserViewModel.cs
using Technical_Application.Model;
using System.Collections.ObjectModel;
namespace Technical_Application.ViewModel
{
public class UserViewModel
{
public ObservableCollection<User> Users
{
get;
set;
}
public void LoadUser()
{
ObservableCollection<User> users = new ObservableCollection<User>();
users.Add(new User { Id = 1});
users.Add(new User { AccessID = 1 });
users.Add(new User { Username = "User1" });
Users = users;
}
}
}
UserView.xaml
<UserControl x:Class="Technical_Application.Views.UserView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Technical_Application.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel HorizontalAlignment = "Left">
<ItemsControl ItemsSource = "{Binding Path = Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<TextBlock Text = "{Binding Path = Username, Mode = OneWay}"
Margin = "0 5 3 5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
**Dashboard.xaml**
<Window x:Class="Technical_Application.Dashboard"
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:Technical_Application"
xmlns:views = "clr-namespace:Technical_Application.Views"
mc:Ignorable="d"
Title="Dashboard" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<views:UserView x:Name="UserView" Loaded="UserView_Loaded"/>
</Grid>
</Window>
Dashboard.xaml.cs
namespace Technical_Application
{
/// <summary>
/// Interaction logic for Dashboard.xaml
/// </summary>
public partial class Dashboard : Window
{
public Dashboard()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void UserView_Loaded(object sender, RoutedEventArgs e)
{
Technical_Application.ViewModel.UserViewModel u =
new Technical_Application.ViewModel.UserViewModel();
u.LoadUser();
UserView.DataContext = u;
}
}
}
Next...
I need to figure out how to store information using a button click.
First of all let me recommend that you use MVVM pattern which will make your life easier now and in the future.
Now for your case, you can't directly access a random objects in C# (including forms) unless you have their reference.
the forms usually have a parent/child relationship which you can use to pass information between them. So if you do this in your MainWindow.xaml.cs:
Dashboard dash = new Dashboard(u);
dash.Show();
you can receive the user object in the constructor of the dashboard form. It is possible to act in the other direction and pass information from the child to the parent.

Usercontrol not showing in designer

I have a custom user control:
<UserControl Name="ddTextBox" x:Class="UserControlsLibrary.DDTextBox"
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:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignWidth="250" FontFamily="Tahoma" FontSize="14" MinHeight="25" Height="275">
<StackPanel>
<TextBox Name="ControlText" Height="25" HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<ListBox Name="ControlList" VerticalAlignment="Top" MaxHeight="250" HorizontalAlignment="Stretch" Visibility="Collapsed" ItemsSource="{Binding Path=DList, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
With the code behind:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserControlsLibrary
{
public partial class DDTextBox : UserControl, INotifyPropertyChanged
{
private string _text;
public string Text { get => _text ?? string.Empty; set { _text = value; OnPropertyChanged(); } }
public DDTextBox()
{
InitializeComponent();
ControlText.DataContext = this;
ControlList.DataContext = this;
ControlText.TextChanged += TextChanged;
ControlText.GotKeyboardFocus += KeyboardFocusChanged;
ControlText.LostKeyboardFocus += KeyboardFocusChanged;
ControlList.SelectionChanged += SelectionChanged;
ControlList.GotKeyboardFocus += KeyboardFocusChanged;
ControlList.LostKeyboardFocus += KeyboardFocusChanged;
IsKeyboardFocusWithinChanged += DDTextBox_IsKeyboardFocusWithinChanged;
}
private void TextChanged(object sender, TextChangedEventArgs e)
{ DItemsSource = ItemsSource.Cast<string>().ToList().FindAll(r => r.IndexOf(( (TextBox)sender ).Text, StringComparison.OrdinalIgnoreCase) >= 0); }
private void SelectionChanged(object sender, SelectionChangedEventArgs e)
{ Text = ( e.AddedItems.Count > 0 ) ? e.AddedItems[0].ToString() : string.Empty; ( (ListBox)sender ).Visibility = Visibility.Collapsed; }
private void KeyboardFocusChanged(object sender, KeyboardFocusChangedEventArgs e)
{
ControlList.Visibility = ( e.NewFocus == ControlList || e.NewFocus == ControlText ) ? Visibility.Visible : Visibility.Collapsed;
if ( e.OldFocus?.GetType() == typeof(ListBox) ) ( (ListBox)e.OldFocus ).SelectedIndex = -1;
}
private void DDTextBox_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ( (bool)e.OldValue == true && (bool)e.NewValue == false )
{ RaiseEvent(new RoutedEventArgs(UserControlLostFocusEvent, this)); }
}
private void ControlLostFocus(object sender, RoutedEventArgs e)
{ if ( !ControlList.IsFocused && !ControlText.IsFocused ) { ControlList.Visibility = Visibility.Collapsed; ControlList.SelectedIndex = -1; } }
public event RoutedEventHandler UserControlLostFocus
{
add { AddHandler(UserControlLostFocusEvent, value); }
remove { RemoveHandler(UserControlLostFocusEvent, value); }
}
public IEnumerable ItemsSource
{
private get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); SetValue(DItemsSourceProperty, value); }
}
/// <summary>
/// Dynamically generated ItemsSource based on Text property. Changes made to this may be lost. List changes should be made to the ItemsSource.
/// </summary>
public IEnumerable DItemsSource
{
private get { return (IEnumerable)GetValue(DItemsSourceProperty); }
set { SetValue(DItemsSourceProperty, value); }
}
public static DependencyProperty DItemsSourceProperty => dItemsSourceProperty;
public static DependencyProperty ItemsSourceProperty => itemsSourceProperty;
public static RoutedEvent UserControlLostFocusEvent => userControlLostFocusEvent;
private static readonly DependencyProperty itemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(DDTextBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static readonly DependencyProperty dItemsSourceProperty = DependencyProperty.Register("DItemsSource", typeof(IEnumerable), typeof(DDTextBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static readonly RoutedEvent userControlLostFocusEvent = EventManager.RegisterRoutedEvent("UserControlLostFocus", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DDTextBox));
private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{ if ( sender is DDTextBox control ) control.OnItemsSourceChanged((IEnumerable)e.NewValue, (IEnumerable)e.OldValue); }
private void OnItemsSourceChanged(IEnumerable newValue, IEnumerable oldValue)
{
if ( oldValue is INotifyCollectionChanged oldValueINotifyCollectionChanged )
{ oldValueINotifyCollectionChanged.CollectionChanged -= new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); }
if ( newValue is INotifyCollectionChanged newValueINotifyCollectionChanged )
{ newValueINotifyCollectionChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); }
}
void NewValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string Name = "")
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); }
}
}
Referenced in my main project and added to the main window:
<Window Name="Main" x:Class="POSystem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCtrls="clr-namespace:UserControlsLibrary;assembly=UserControlsLibrary"
Title="Purchase Order" Height="850" Width="1100" Background="#FFD8D0A9">
<Grid Name="TheGrid" Focusable="True" MouseDown="ClearFocus_OnClick" Background="Transparent">
<myCtrls:DDTextBox x:Name="ItemsBox" Width="300" VerticalAlignment="Top" HorizontalAlignment="Left" Panel.ZIndex="1"/>
</Grid>
</Window>
At runtime everything shows up and works as it is supposed too, however, in the window designer of the main page it does not show anything other than an outline indicating that the control is there. This outline can range from a single line at the location (only way all the functionality works proper) or if I add a minimum or just straight height it shows an empty outline of the control. How do I get this to show the (supposed to be) visible at all times textbox within the user control in the main window designer?

How do I serialize the selection of an RTB with a custom control that has a bound DependencyProperty?

I've got a sample reproduction that crashes despite protection.
It looks like when I try manually serializing a TextRange that contains a custom control that contains a bound DependencyProperty crashes because it fails an assert, because the value of the property isn't assignable to the property, because it's an expression.
Is this a bug in my code somewhere? Is this a bug in the serializer? How can I fix this? How can I make it serialize my custom class in a selection?
I've narrowed it down to this:
Xaml
<Window x:Class="SaveCustomCrash.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SaveCustomCrash"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Click="CrashClick">Crash</Button>
<RichTextBox Name="rtb">
<FlowDocument>
<Table>
<TableRowGroup>
<c:CustomRow Settable="{Binding IsVisible,ElementName=rtb}">
<TableCell>
<Paragraph>Stuff</Paragraph>
</TableCell>
</c:CustomRow>
</TableRowGroup>
</Table>
</FlowDocument>
</RichTextBox>
</StackPanel>
</Window>
Codebehind:
using System.IO;
using System.Windows;
using System.Windows.Documents;
namespace SaveCustomCrash
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void CrashClick(object sender, RoutedEventArgs e)
{
rtb.SelectAll();
using (var memoryStream = new MemoryStream())
{
if (!rtb.Selection.CanSave(DataFormats.Xaml))
{
MessageBox.Show("Can't Save"); // this doesn't get hit.
return;
}
try
{
rtb.Selection.Save(memoryStream, DataFormats.Xaml, true);
}
catch // apparently it can't catch this exception.
{
}
memoryStream.Flush();
memoryStream.Position = 0;
using (var streamReader = new StreamReader(memoryStream))
{
MessageBox.Show("Xaml: " + streamReader.ReadToEnd());
}
}
}
}
public class CustomRow : TableRow
{
public static readonly DependencyProperty SettableProperty =
DependencyProperty.Register("Settable", typeof (bool), typeof (CustomRow), new PropertyMetadata(default(bool)));
public bool Settable
{
get { return (bool) GetValue(SettableProperty); }
set { SetValue(SettableProperty, value); }
}
}
}
While saving selected text into XAML you can pass last parameter as false which stands for preserveTextElements.
rtb.Selection.Save(memoryStream, DataFormats.Xaml, false);

I can't Data Bind to a local variable in WPF/XAML

I want a textbox to display the value of a variable when I click it (an iteration of 1 to 100), I do not know what I am doing Wrong:
When I run the project nothing is displayed in the text box.
What is the best way to display variables in a text box?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace dataBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string myText { get; set; }
public void Button_Click_1(object sender, RoutedEventArgs e)
{
int i = 0;
for (i = 0; i < 100; i++)
{
myText = i.ToString();
}
}
}
}
XAML:
<Window x:Class="dataBindingTest.MainWindow"
Name="windowElement"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Height="106" Margin="71,95,0,0" VerticalAlignment="Top" Width="125" Click="Button_Click_1"/>
<TextBlock x:Name="myTextBox" HorizontalAlignment="Left" Height="106" Margin="270,95,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="187" Text= "{Binding myText, ElementName=windowElement}" />
</Grid>
</Window>
Your current myText property has no way of notifying the WPF binding system when its value has changed, so the TextBlock wont be updated.
If you make it a dependency property instead it automatically implements change notification, and the changes to the property will be reflected in the TextBlock.
So if you replace public string myText { get; set; } with all of this code it should work:
public string myText
{
get { return (string)GetValue(myTextProperty); }
set { SetValue(myTextProperty, value); }
}
// Using a DependencyProperty as the backing store for myText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty myTextProperty =
DependencyProperty.Register("myText", typeof(string), typeof(Window1), new PropertyMetadata(null));
implement INotifyPropertyChanged:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
this.InitializeComponent();
}
private string _txt;
public string txt
{
get
{
return _txt;
}
set
{
if (_txt != value)
{
_txt = value;
OnPropertyChanged("txt");
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
txt = "changed text";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<TextBox Text="{Binding txt}"/>
<Button Click="Button_Click">yes</Button>
and don't forget about adding the DataContext property of your window:
<Window ... DataContext="{Binding RelativeSource={RelativeSource Self}}"/>
Try this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public string myText { get; set; }
public void Button_Click_1(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += delegate
{
int i = 0;
for (i = 0; i < 100; i++)
{
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() => { myText = i.ToString(); OnPropertyChanged("myText"); }));
Thread.Sleep(100);
}
};
bw.RunWorkerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
XAML file:
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Height="106" Margin="71,95,0,0" VerticalAlignment="Top" Width="125" Click="Button_Click_1"/>
<TextBlock x:Name="myTextBox"
HorizontalAlignment="Right" Height="106" Margin="0,95,46,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="187"
Text= "{Binding myText}" />
</Grid>
You should implement INotifyPropertyChanged in your "MainWindow" so your "myTextBlock" can automatically pick up changes from your data and update.
So your "MainWindow" should look like:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private string _myText;
public string myText {
get{return _myText;}
set{_myText = value;
if(PropertyChanged!=null) PropertyChanged(this, new PropertyChangedEventArgs("myText")) ;
}
}
public event PropertyChangedEventHandler PropertyChanged;
etc.....
}
You need to make the property tell the binding that it has updated. The standard way to do this is via:
Implementing INotifyPropertyChanged
Making the myText property a DependencyProperty
Another maybe less used way is to raise the event manually, like this:
public void Button_Click_1(object sender, RoutedEventArgs e)
{
myText = "Clicked";
BindingOperations.GetBindingExpressionBase(myTextBox, TextBlock.TextProperty).UpdateTarget();
}
Note that your TextBlock has the confusing name myTextBox

Categories