How to bind the content of a label to the class2 property PropName?
Class2 is not directly being used in Mainwindlow.xmal.cs.
Class1 is being used in Mainwindow.xmal.cs
And Class2 is being used in Class1.
Here is the code I'm using:
class Class2:INotifyPropertyChanged
{
string _PropName;
public string PropName
{
get
{
return this._PropName;
}
set
{
this._PropName = value;
OnPropertyChanged("PropName");
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class MainWindow : Window,INotifyPropertyChanged
{
Class1 class1ob;
public MainWindow()
{
InitializeComponent();
class1ob = new Class1();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
class1ob.changeProp();
}
}
I want to bind the content of a label eith the Class2 property - PropName.
How can I do that?
Try this.
XAML
....
<Label Name="label" Content="{Binding Path=PropName}"/>
....
On your WindowLoad set DataContext for Label.
label.DataContext = class1ob.class2ob;//instance of class
Related
I have a call
await Shell.Current.GoToAsync($"{viewPath}");
and also defined all the pages with routes and all the viewModels:
services.AddSingleton<MainPage>();
services.AddSingleton<ItemPage>();
services.AddSingleton<ItemsPage>();
services.AddSingleton<MainPageViewModel>();
services.AddSingleton<ItemPageViewModel>();
services.AddSingleton<ItemsPageViewModel>();
services.AddSingleton<ItemsPageModifiedViewModel>();
my idea is to call i.e ItemsPage with ItemsPageViewModel which is default, and in some scenarios i want to call ItemsPage with ItemsPageModifiedViewModel .
Is it possible to override the BindingContext?
You could use a static viewmodel for the same model.
I have a shell with tabs of Page1, Page2. And Page 2 has a button to do the navigation.
Page1ViewModel:
public class Page1ViewModel : INotifyPropertyChanged
{
public string _str;
public event PropertyChangedEventHandler PropertyChanged;
public string str
{
get
{
return _str;
}
set
{
_str = value;
OnPropertyChanged("str");
}
}
public Page1ViewModel()
{
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Page1.xaml:
<Label Text="{Binding str}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
Page1 code behind:
public static Page1ViewModel viewmodel { get; set; }
public Page1()
{
InitializeComponent();
viewmodel = new Page1ViewModel();
viewmodel.str = "hello";
this.BindingContext = viewmodel;
}
Page2 code behind:
private async void Button_Clicked(object sender, EventArgs e)
{
Page1.viewmodel.str = "test";
await Shell.Current.GoToAsync("//Page1");
}
I have played around with this for a while and decided to see if someone can help, I have set in the constructor of StatusInfo the DataContext = this and didn't work. When I write a string to ScreenStatusBarText it does call the OnPropertyChanged method but every time the PropertyChanged value is null. I The status block I have at the bottom of the screen. I have a tab section above this stack panel that has many components that use bindings and work.
Screen Code
<StackPanel Margin="0,1047,0,0">
<Grid Name="StatusBarItemGrid">
<TextBlock Name="StatusBarText" Text="may the force be with you" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
</Grid>
</StackPanel>
Data Model:
public partial class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText;
public StatusInfo()
{
BindScreenStatusBarText();
screenStatusBarText = "Initialized";
}
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged("StatusBarText");
}
}
private void BindScreenStatusBarText()
{
Binding b = new Binding();
b.Source = screenStatusBarText;
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("StatusBarText");
MainWindow.mainWindow.StatusBarText.SetBinding(TextBlock.TextProperty, b);
MainWindow.mainWindow.StatusBarText.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
My main :
public partial class MainWindow : Window
{
public static StatusInfo status;
public MainWindow()
{
InitializeComponent();
SourceInitialized += MainWindow_SourceInitialized;
}
private void MainWindow_SourceInitialized(object sender, EventArgs e)
{
SetUpDisplay();
}
private void SetUpDisplay()
{
status = new StatusInfo();
}
}
Set the Binding in XAML instead of code behind:
<TextBlock Text="{Binding ScreenStatusBarText}" />
And use a view model like
public class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText = "Initialized";
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
with an instance of the view model class assigned to the MainWindow's DataContext:
private readonly StatusInfo statusInfo = new StatusInfo();
public MainWindow()
{
InitializeComponent();
DataContext = statusInfo;
}
You may now access the view model class at any time later, e.g. in an event handler of an element of MainWindow:
statusInfo.ScreenStatusBarText = "Something";
I think your going to struggle doing your binding in code behind.
Having said that, with regards to why your PropertyChanged value is null. You've simply made a typo, as-is you're notifying subscribers that a property that doesn't exist has changed. One solution to avoid such typos is to use nameof.
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
It occurred to me you may also have meant that your event was null. This simply means you don't have any subscribers. See Why is my "Event" always null?.
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) // I have a subscriber.
handler(this, new PropertyChangedEventArgs(propertyName));
}
How do I bind FontSize for WPF TextBox in XAML to a class member variable?
I have a collection of fonts that I use through the application.
I would like to change the values of those fonts dynamically in my code behind and then have the changes reflected during runtime.
How do I achieve this?
Here is what my class definition looks like
public ClassFoo
{
public double FontSize {get; set;}
}
This is how I define my class in MainWindow.xaml.cs:
public ClassFoo SampleClass;
Here is my what my XAML looks like:
<TextBlock Name="txtSample" Text="SomeText"
FontSize="{Binding SampleClass.FontSize}"/>
Then at runtime, I instantiate the class and initialize it:
SampleClass = new ClassFoo()
{
FontSize = 16;
}
I would create it like that:
public class MainWindow : Page
{
public Foo Foo { get; set; }
public MainWindow()
{
DataContext = this;
}
}
public class Foo : INotifyPropertyChanged
{
private double _fontSize;
public double FontSize
{
get { return _fontSize; }
set
{
_fontSize = value;
OnPropertyChanged(nameof(FontSize));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and then call it like:
<TextBlock Name="txtSample" Text="SomeText"
FontSize="{Binding Foo.FontSize}"/>
Most likely you need a DataContext = this; in your constructor for Mainwindow.xaml.cs. You also need in Mainwindow.xaml.cs that returns SampleClass.
You can only bind to public properties so the first thing to do would be to make SampleClass a property:
public ClassFoo SampleClass { get; set; }
And if you intend to set it dynamically at runtime after the constructor of the window has returned, the window should implement the INotifyPropertyChanged interface and raise change notfications for the taget property to get automatically updated.
Finally the source of the binding must be set to the window somehow. You could set the Source property of the binding explicitly or set the DataContext of the TextBlock or any of its parent element to an instance of the window.
Try this implementation of the MainWindow class together with the XAML markup you posted:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
SampleClass = new ClassFoo()
{
FontSize = 16
};
}
private ClassFoo _sampleClass;
public ClassFoo SampleClass
{
get { return _sampleClass; }
set { _sampleClass = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Trying to make my first application with the simple logging function to the TextBox on main form.
To implement logging, I need to get the TextBox object into the logger's class.
Prob - can't do that :) currently have no error, but as I understand the text value of TextBox is binding to my ViewModel, because getting 'null reference' exception trying to execute.
Logger.cs
public class Logger : TextWriter
{
TextBox textBox = ViewModel.LogBox;
public override void Write(char value)
{
base.Write(value);
textBox.Dispatcher.BeginInvoke(new Action(() =>
{
textBox.AppendText(value.ToString());
}));
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
ViewModel.cs
public class ViewModel
{
public int ThreadCount { get; set; }
public int ProxyTimeout { get; set; }
public static TextBox LogBox { get; set; }
//private TextBox _LogBox;
//public TextBox LogBox {
// get { return _LogBox; }
// set {
// _LogBox = value;
// }
//}
}
launching on btn click, MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Logger logger = new Logger();
logger.Write("ewgewgweg");
}
}
MainWindow.xaml
<Window
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:tools"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" x:Class="tools.MainWindow"
mc:Ignorable="d"
Title="Tools" Height="399.387" Width="575.46">
<TextBox x:Name="logBox"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalAlignment="Left" Height="137" Margin="10,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="394" Text="{Binding Path = LogBox, Mode=TwoWay}"/>
You have several issues in your code:
Don't bring controls (TextBox) in your viewmodel, if you do there's no use in trying to do MVVM.
The Text property in XAML has to be of the type String or something that can be converted to a string. You're binding a control, which will result in showing System.Windows.Controls.TextBox (result of .ToString()) on your screen instead of actual text.
Your LogBox property should implement INotifyPropertyChanged
You don't want TwoWay binding, as the text flows from your logger to the UI, you don't need it to flow back. You might even consider using a TextBlock instead or make the control readonly so people can't change the content.
You don't want static properties or static viewmodels, read up on dependency injection on how to pass dependencies.
You will be flooding your UI thread by appending your characters one by one. Consider using another implementation (but I won't go deeper into this for this answer).
Keeping all above in mind, I transformed your code to this.
MainWindow.xaml
<TextBox x:Name="logBox"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="137" Margin="10,222,0,0"
TextWrapping="Wrap" Width="394" Text="{Binding Path = LogBox}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private Logger _logger;
public MainWindow()
{
InitializeComponent();
var viewModel = new ViewModel();
DataContext = viewModel;
_logger = new Logger(viewModel); // passing ViewModel through Dependency Injection
}
private void button1_Click(object sender, RoutedEventArgs e)
{
_logger.Write("ewgewgweg");
}
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public int ThreadCount { get; set; }
public int ProxyTimeout { get; set; }
private string _logBox;
public string LogBox
{
get { return _logBox; }
set
{
_logBox = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Logger.cs
public class Logger : TextWriter
{
private readonly ViewModel _viewModel;
public Logger(ViewModel viewModel)
{
_viewModel = viewModel;
}
public override void Write(char value)
{
base.Write(value);
_viewModel.LogBox += value;
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
You can use string instead of TextBox as follow as
In view model class
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _logBox;
public string LogBox
{
get {return _logBox;}
set
{
if(value != _logBox)
{
_logBox=value;
OnPropertyChanged("LogBox");
}
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and in writer method you just
public void writer (string str)
{
ViewModel.LogBox = str;
}
You can define ViewModel as static or create new object from ViewModel and access the object in logger class as you want!
hope this helped.
I don't understand why my rectangles are not being shown.
I made the xaml, and data binded the canvas, and init properly.
What am I missing such that it only shows a blank screen.
It should show a digital figure 8.
MODEL:
namespace Final
{
class Model : INotifyPropertyChanged
{
// define our property chage event handler, part of data binding
public event PropertyChangedEventHandler PropertyChanged;
// implements method for data binding to any and all properties
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private double _topTopHorizontal;
public double topTopHorizontal
{
get { return _topTopHorizontal; }
set
{
_topTopHorizontal = value;
OnPropertyChanged("topTopHorizontal");
}
}
private double _leftTopHorizontal;
public double leftTopHorizontal
{
get { return _leftTopHorizontal; }
set
{
_leftTopHorizontal = value;
OnPropertyChanged("leftTopHorizontal");
}
}
public void initModel()
{
topTopHorizontal = 50;
leftTopHorizontal = 50;
}
}
}
Main
public partial class MainWindow : Window
{
private Model model;
public MainWindow()
{
InitializeComponent();
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// create an instance of our Model
model = new Model();
model.initModel();
}
}
}
You haven't set the DataContext for this window.
In constructor add:
public MainWindow()
{
InitializeComponent();
model = new Model();
DataContext = model;
}
Therefor, your window can access "leftTopHorizontal" and "topTopHorizontal".
And in your xaml change:
Canvas.Top ="{Binding topTopHorizontal}"
Canvas.Left="{Binding leftTopHorizontal}"
with:
Canvas.Top ="{Binding model.topTopHorizontal}"
Canvas.Left="{Binding model.leftTopHorizontal}"