Can't access public property on user control wpf c# - c#

I'm trying to access a public property on a user control but I am getting this message. I don't think I would need to initialize the user control right? When I try to access the public property DirectorySetter.DirectoryPath I would get this message:
An object reference is required for the non-static field, method, or
property 'DirectorySetter.DirectoryPath'
Here is my user control code-behind:
public partial class DirectorySetter : UserControl
{
public DirectorySetter()
{
InitializeComponent();
}
public string DirectoryPath
{
get
{
return txtDirectoryPath.Text;
}
set
{
txtDirectoryPath.Text = value;
}
}
}
Here is the xaml that uses the user control:
<Page
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:PhotoOrganizer.Pages"
xmlns:UserControls="clr-namespace:PhotoOrganizer.UserControls" x:Class="PhotoOrganizer.Pages.PhotoDirectoryPath"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="PhotoDirectoryPath">
<Grid>
<TextBlock HorizontalAlignment="Left" Margin="69,91,0,0" TextWrapping="Wrap" Text="Set your photo directory path" VerticalAlignment="Top"/>
<UserControls:DirectorySetter HorizontalAlignment="Left" Margin="22,135,0,0" VerticalAlignment="Top"/>
<Button Name="btnSave" Content="Save" HorizontalAlignment="Left" Margin="155,178,0,0" VerticalAlignment="Top" Width="75" Click="btnSave_Click"/>
</Grid>
</Page>
Any suggestion or help would be great!

You didn't post the code where there error actually happens, where you try to access that "public property"
So I might just guess that you're trying to do something like
DirectorySetter.DirectoryPath = "asd";
which won't work since your class and your property aren't static.
What you can do though, is (xaml):
<UserControls:DirectorySetter x:Name="myUserControl"/>
Code behind:
var s = (myUserControl as DirectorySetter).DirectoryPath ;

If you want to access your Property from xaml and or bind to it, you will need to implement a Dependency Property in your UserControl Class
// Dependency Property
public static readonly DependencyProperty DirectoryPathProperty =
DependencyProperty.Register( "DirectoryPath", typeof(string),
typeof(DirectorySetter), new FrameworkPropertyMetadata(string.Empty));
// .NET Property wrapper
public string DirectoryPath
{
get { return (string)GetValue(DependencyProperty ); }
set { SetValue(DependencyProperty , value); }
}
Additional Resources from Msdn
Dependency properties overview

Related

C# Community Toolkit Mvvm Source Generator: ObservableProperty capitalises property name

I'm just getting started with the toolkit and I'm trying to generate a simple ObservableProperty to use with WPF. I create a usercontrol:
<UserControl x:Class="WPF_test.StatusControl"
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:WPF_test"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
</Grid>
</UserControl>
and a ViewModel:
using System;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;
namespace WPF_test
{
[ObservableObject]
public partial class StatusControlViewModel
{
[ObservableProperty]
private String? testData;
}
}
I embed the control into the MainWindow and set the datacontext in codebehind:
public partial class MainWindow : Window
{
StatusControlViewModel model;
public MainWindow()
{
InitializeComponent();
model = new StatusControlViewModel();
status.DataContext = model;
model.testData = "test";
}
}
but I see that model.testData is inaccessible due to its protection level. When I comment this line out in order to get the code to run I get a binding error saying that testData cannot be found.
This is the generated code:
namespace WPF_test
{
partial class StatusControlViewModel
{
/// <inheritdoc cref="testData"/>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public string? TestData
{
get => testData;
set
{
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
{
OnTestDataChanging(value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
testData = value;
OnTestDataChanged(value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
}
}
}
/// <summary>Executes the logic for when <see cref="TestData"/> is changing.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
partial void OnTestDataChanging(string? value);
/// <summary>Executes the logic for when <see cref="TestData"/> just changed.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
partial void OnTestDataChanged(string? value);
}
}
It seems that the toolkit is capitalising my property name. I can make the databinding work by capitalising the property name in the control XAML:
<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
and similarly access the model property:
model.TestData = "test";
Is there a way to use the toolkit so that the property is accessed in the original form, i.e.
<TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
not
<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
? I think it's going to be confusing otherwise.
No.
There is no way "to use the toolkit so that the property is accessed in the original form"
Because that's a field and not a property.
The code generator is literally generating some code.
The property name cannot be the same as your backing field. testData is a field. You cannot bind to a field.
[ObservableProperty]
private String? testData;
Generates a TestData property in the partial class.
public string? TestData
{
get => testData;
set
{
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
{
OnTestDataChanging(value);
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
testData = value;
OnTestDataChanged(value);
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
}
}
}
You can only bind to public properties so you need what that generates in order to bind. No property means no binding.
Binding:
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0
Property:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
In the above _seconds is a field.
Hours is a property.
The difference is properties have getters and (optional) setters.
Having said all that.
You are not forced to use that attribute.
You can create your properties manually.
Then you can have whatever case you like for your property.
I suggest you learn to like upper case properties though.
I've worked for a lot of clients. The standard for property names has always been to start with an upper case letter.
PS
[Relaycommand] generates an upper class property with "Command" appended.

Binding data to custom UserControl

I want bind ObservableCollection my custom UserControl but when I want add new element and I almost done but when I want add new element to collection my app crash with no reason. I thought maybe it is variable inconsistency. But I came with nothig trying with object/String/string. Maybe I done something wrong at start?
Custom UserControl XAML
<UserControl x:Class="backtrackPrototype.checklistItem"
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"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="80" d:DesignWidth="480">
<Grid x:Name="LayoutRoot">
<TextBlock
x:Name="label"
MaxHeight="407"
HorizontalAlignment="Left"
Margin="0,17,0,16"
VerticalAlignment="Center"
Text="{Binding Path=Title, ElementName=checklistItem}"/>
<CheckBox x:Name="checkbox" HorizontalAlignment="Right" VerticalAlignment="Center" Checked="checkbox_Checked" Unchecked="checkbox_Unchecked" />
</Grid>
</UserControl>
Custom UserControl
public partial class checklistItem : UserControl
{
public string Title
{
get { return (string)this.GetValue(TitleProperty); }
set { this.SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(checklistItem), new PropertyMetadata(0));
public checklistItem()
{
InitializeComponent();
}
public bool isChecked()
{
if ((bool)checkbox.IsChecked) return true;
return false;
}
private void checkbox_Checked(object sender, RoutedEventArgs e)
{
//title.Foreground = Application.Current.Resources["PhoneAccentColor"];
label.Foreground = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
}
private void checkbox_Unchecked(object sender, RoutedEventArgs e)
{
label.Foreground = new SolidColorBrush(Colors.White);
}
}
XAML in main page
<ListBox Height="479" ItemsSource="{Binding}" x:Name="CheckList">
<ListBox.ItemTemplate>
<DataTemplate>
<local:checklistItem Title="{Binding Title}" Width="397"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox
CS in main page
public ObservableCollection ListItems = new ObservableCollection();
ListItem item = new ListItem("Nowe",false);
ListItems.Add(item);
CheckList.DataContext = ListItems;
And finally List
public class ListItem
{
public string Title { get; set; }
public bool Checked { get; set; }
public ListItem(string title, bool isChecked=false)
{
Title = title;
Checked = isChecked;
}
}
I need someone who will look at code with fresh mind. Thank you.
UPDATE:
What's more for accessing controls in my CustomControl I use first anwser in that question .
Also I update my UserControl XAML.
UPDATE V2
When I'm using <TextBox Title="{Binding Title}" Width="397"/> insted of local:checklistItem it work just fine.
UPDATE V3
OK turns out that I didn't add proper DataContext to my UserControl so now checklistItem constructor looks like this
public checklistItem() {
InitializeComponent();
this.DataContext = this;
}
And controls are adding correctly but now binding for Title is not working. Because when I hardcode some title it working, binding not. But the same binding is working for default textbox.
I think you need to set the itemsSource rather than the DataContext. Setting DataContext on a Listbox will NOT generate the templated items
take out
ItemsSource="{Binding}"
from the Xaml
and change
CheckList.DataContext = ListItems;
to
CheckList.ItemsSource = ListItems;
I think that perhaps your binding in the XAML is incorrect. Your element name in the binding is LayoutRoot which is the name of your grid not your UserControl.
It is probably trying to find a property on the Grid called Title which it does not have.

Binding a WPF control to an Object

I have an object called "Employee", as follows:
class Employee
{
public string EmpName { get; set; }
public string Surname { get; set; }
public Employee(string Name, string Surname) : base()
{
this.EmpName = EmpName;
this.Surname = Surname;
}
public Employee()
{
}
}
I also have this simple "EmployeeControl" here. Just two labels in a grid:
<UserControl x:Class="LabelBindingTest.EmployeeCtrl"
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"
Name="EmpCtrl"
d:DesignHeight="120" d:DesignWidth="411">
<Grid>
<Label Content="{Binding ElementName=EmpCtrl, Path=EmpName}" Height="28" HorizontalAlignment="Left" Name="label1" VerticalAlignment="Top" Width="81" />
<Label Content="{Binding ElementName=EmpCtrl, Path=Surname}" Height="28" HorizontalAlignment="Right" Name="label2" VerticalAlignment="Top" Width="81" />
</Grid>
</UserControl>
(Edit) EmployeeControl codebehind:
public partial class EmployeeCtrl : UserControl
{
Employee thisEmployee = new Employee();
public string EmpName
{
get
{
return thisEmployee.EmpName;
}
set
{
thisEmployee.EmpName = value;
}
}
public string Surname
{
get
{
return thisEmployee.EmpName;
}
set
{
thisEmployee.EmpName = value;
}
}
public EmployeeCtrl()
{
InitializeComponent();
}
}
Now, what I'm trying to do is add "EmployeeControl"s to my window and bind them to Employee objects.
Here's my "window" code:
<Window x:Class="LabelBindingTest.MainWindow"
Name="myWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:LabelBindingTest">
<Grid>
<my:EmployeeCtrl EmpName="Daniel" Surname="Hammond" HorizontalAlignment="Left" Margin="23,26,0,0" x:Name="employeeCtrl1" VerticalAlignment="Top" Height="97" Width="429" />
</Grid>
</Window>
I've tried a variety of things but I just can't get it to work. It compiles just fine but the label is empty. I just want to attach my "Employee" object to the "EmployeeCtrl" control. Any ideas on how I go around this? I've read a lot about binding today and I'm still scratching my head. I'm not sure if I need to implement INotifyPropertyChanged as I only set the employee names before runtime for now.
(Edit): I've downloaded Caliburn.Micro and used the same code in the last answer. Same result, empty window.
Here's the whole project. All of it's code should be pasted in this question, though. This is just for convenience sake.
http://www.mediafire.com/?gux3573rz64mupe
Ok, firstly I would strongly recommend considering MVVM as your application gets more complicated, and use an MVVM framework if you're using MVVM. I would recommend Caliburn.Micro which makes view composition like you're doing much much easier. There are other MVVM frameworks available, so evaluate them all.
For your issue, the problem is that your user control doesn't implement its properties as dependency properties. In the XAML, you're setting the EmpName and Surname properties of the user control, but the UI doesn't know that their values have changed after the user control is constructed, and therefore doesn't update itself.
You implement INotifyPropetyChanged on view models when using the MVVM pattern, and dependency properties on user controls. Both of these technqiues will notify the UI of a change and force it to update itself.
So for your user control you can do:
public partial class EmployeeCtrl : UserControl
{
public static readonly DependencyProperty EmpNameProperty =
DependencyProperty.Register("EmpName", typeof(string),
typeof(EmployeeCtrl), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty SurnameProperty =
DependencyProperty.Register("Surname", typeof(string),
typeof(EmployeeCtrl), new FrameworkPropertyMetadata(null));
public EmployeeCtrl()
{
this.InitializeComponent();
}
public string EmpName
{
get { return (string)GetValue(EmpNameProperty); }
set { SetValue(EmpNameProperty, value); }
}
public string Surname
{
get { return (string)GetValue(SurnameProperty); }
set { SetValue(SurnameProperty, value); }
}
}
You don't actually need your Employee class at all at this stage as you aren't using it, but ideally it should implement INotifyPropertyChanged if it's property values will change after construction and you have an instance of it is bound to the UI and you wish the UI to be updated.
you need to implement INotifyPropertyChanged on your EmployeeCtrl's EmpName and Surname property as the Label in this user control needs to be updated about the value change in this property value so that label can update its value. Hence you have to implement Notify change on EmpName and Surname property of the EmployeeCtrl or simply use dependency property for this two property. Hope this helps!!!

Simple Dependency Property and UserControl issues in C#

My end goal is to expose the Text value of a TextBox that I have in a UserControl, from the UserControl's call in XAML.
<my:UserControl SetCustomText="Blah blah this is variable">
would render the UserControl with that TextBox's text filed in.
I've been working at it using various examples but I always end up with "The Property SetCustomText was not found in type UserControl"
Example of how you can do this:
<UserControl x:Class="Test.UserControls.MyUserControl1"
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"
Name="control">
<Grid>
<!-- Text is being bound to outward representative property -->
<TextBox Text="{Binding MyTextProperty, ElementName=control}"/>
</Grid>
</UserControl>
public partial class MyUserControl1 : UserControl
{
// The dependency property which will be accessible on the UserControl
public static readonly DependencyProperty MyTextPropertyProperty =
DependencyProperty.Register("MyTextProperty", typeof(string), typeof(MyUserControl1), new UIPropertyMetadata(String.Empty));
public string MyTextProperty
{
get { return (string)GetValue(MyTextPropertyProperty); }
set { SetValue(MyTextPropertyProperty, value); }
}
public MyUserControl1()
{
InitializeComponent();
}
}
<uc:MyUserControl1 MyTextProperty="Text goes here"/>

WPF Binding to variable / DependencyProperty

I'm playing around with WPF Binding and variables. Apparently one can only bind DependencyProperties. I have come up with the following, which works perfectly fine:
The code-behind file:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
//set { this.SetValue(TestProperty, "BBB"); }
}
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test", typeof(string), typeof(MainWindow), new PropertyMetadata("CCC"));
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Test);
Test = "AAA";
MessageBox.Show(Test);
}
}
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,86,0,0" Name="textBox1" VerticalAlignment="Top" Width="152"
Text="{Binding Test, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="320,85,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,138,0,0" Name="textBox2" Text="{Binding Test, Mode=TwoWay}" VerticalAlignment="Top" Width="152" />
</Grid>
The two TextBoxes update one an other. And the Button sets them to "AAA".
But now I replaced the Setter function with the one that is commented out (simulating some manipulation of the given value). I would expect that whenever the property value is changed it will be reset to "BBB". It does so when you press the button, that is when you set the property in code. But it does for some reason not affect the WPF Bindings, that is you can change the TextBox contents and thus the property, but apparently the Setter is never called.
I wonder why that is so, and how one would go about to achive the expected behaviour.
The CLR Property wrapper for a Dependency Property is never guaranteed to be called and therefore, you should never place any additional logic there. Whenever you need additional logic when a DP is changed, you should use the property changed callback.
In your case..
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(string),
typeof(MainWindow),
new PropertyMetadata("CCC", TestPropertyChanged));
private static void TestPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MainWindow mainWindow = source as MainWindow;
string newValue = e.NewValue as string;
// Do additional logic
}
Your change will not affect the binding because the XAML will call SetValue directly, instead of calling your property setter.That is how the dependency property system works.When a dependency property is registered a default value can be specified.This value is returned from GetValue and is the default value for your property.
Check the link below and read through to Robert Rossney's post to get a fair overview
WPF: What distinguishes a Dependency Property from a regular CLR Property?
also don't miss
http://msdn.microsoft.com/en-us/library/ms753358.aspx
and
http://msdn.microsoft.com/en-us/library/ms752914.aspx
Also note that unlike in normal CLR properties any custom logic you write in the setter will not be executed in Dependency Properties,instead you have to use the PropertyChangedCallback mechanism
http://blogs.msdn.com/b/delay/archive/2010/03/23/do-one-thing-and-do-it-well-tip-the-clr-wrapper-for-a-dependencyproperty-should-do-its-job-and-nothing-more.aspx

Categories