Changing protection level of newly created object? - c#

Alright, so I have luck of running into a lot of basic problems. I can't figure a way around this particular issue.
This piece of code needs to access "_Player.Name" property of object created in "MainWindow" class.
Edit: Putting up the whole code this time. Here's the Code_Behind where the string is.
public class Code_Behind
{
private static string _Name = "Default";
public class Player
{
public void setName(string name) //Ignore this part, was trying to find a work around here
{
_Name = name;
}
public string Name
{
get { return _Name; }
set
{
_Name = value;
}
}
}
//contentControl is used to store Content properties
//UI elements are bound to Content properties to efficiently change their Content
public class contentControl : INotifyPropertyChanged
{
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public void setEvent(string Event)
{
textBoxContent = Event;
}
public void addEvent(string Event)
{
textBoxContent +="\n" + Event;
}
public class Events
{
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"", window.PlayerName);
}
}
And here is the MainWindow one:
public partial class MainWindow : Window
{
Code_Behind.contentControl cC = new Code_Behind.contentControl();
Code_Behind.contentControl.Events Events = new Code_Behind.contentControl.Events();
Code_Behind.Player _Player = new Code_Behind.Player();
public string GetPlayerName()
{
return _Player.Name;
}
public static string _name = "null";
public MainWindow()
{
this.DataContext = cC;
InitializeComponent();
}

public string GetPlayerName()
{
return _Player.Name
}
Create a method in your MainWindow class. After that you call this method.
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"",
window.GetPlayerName());
You can do it with property too if you want.
public string PlayerName
{
get { return _Player.Name; };
}

The bigger problem you have here is not about accessibility, but not understanding the difference between a class and an object.
MainWindow is a class. It does not represent any specific window. Think of a class like a recipe to create objects. If you had a chocolate chip cookie recipe, you don't eat the recipe, you eat a specific cookie or cookies baked following that recipe.
Your other class first needs to know which specific window you are trying to get the player name from. It needs a reference to a particular MainWindow object.

It looks like you're trying write something like a viewmodel: You've got a player, he has a name, and there's a collection of strings that you think of as "events". I don't understand what the "events" are meant to be, but I implemented my best guess at what I think you seem to be trying to do.
As for this:
public class Events
{
public string EV001 = String.Format("\"Greetings {0}. What can I do for you today?\"", window.PlayerName);
}
I guess you created an instance of MainWindow somewhere, and called it window, but it's defined someplace where it's "out of scope" for that line of code. By analogy, you can't see anything that's behind the next hill, only stuff that's in the valley you're standing in. That's roughly (very roughly, sorry) kind of what scope is about.
But let's move on to my guess at what you're trying to do. This builds, runs, and works. Any questions at all, fire away.
ViewModels.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Player
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class MainViewModel : ViewModelBase
{
#region Player Property
private PlayerViewModel _player = default(PlayerViewModel);
public PlayerViewModel Player
{
get { return _player; }
set
{
if (value != _player)
{
_player = value;
OnPropertyChanged(nameof(Player));
// Change the player for all the existing events.
foreach (var e in Events)
{
e.Player = Player;
}
}
}
}
#endregion Player Property
private ObservableCollection<Event> _events = new ObservableCollection<Event>();
public ObservableCollection<Event> Events
{
get { return _events; }
private set
{
if (value != _events)
{
_events = value;
OnPropertyChanged(nameof(Events));
}
}
}
#region Event Methods
// This is a BIG guess as to what you're trying to do.
public void AddGreeting()
{
// Player is "in scope" because Player is a property of this class.
if (Player == null)
{
throw new Exception("Player is null. You can't greet a player who's not there.");
}
Events.Add(new Event("\"Greetings {0}. What can I do for you today?\"", Player));
}
#endregion Event Methods
}
public class Employee : ViewModelBase
{
#region DisplayLtdOccupationId Property
private bool _displayLtdOccupationId = default(bool);
public bool DisplayLtdOccupationId
{
get { return _displayLtdOccupationId; }
set
{
if (value != _displayLtdOccupationId)
{
_displayLtdOccupationId = value;
OnPropertyChanged(nameof(DisplayLtdOccupationId));
}
}
}
#endregion DisplayLtdOccupationId Property
}
public class Event : ViewModelBase
{
public Event(String format, PlayerViewModel player)
{
_format = format;
Player = player;
}
private String _format = "";
public String Message
{
get { return String.Format(_format, Player.Name); }
}
#region Player Property
private PlayerViewModel _player = default(PlayerViewModel);
public PlayerViewModel Player
{
get { return _player; }
set
{
if (value != _player)
{
_player = value;
OnPropertyChanged(nameof(Player));
// When player changes, his name changes, so that
// means the value of Message will change.
OnPropertyChanged(nameof(Message));
if (_player != null)
{
_player.PropertyChanged += _player_PropertyChanged;
}
}
}
}
private void _player_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(PlayerViewModel.Name):
OnPropertyChanged(nameof(Message));
break;
}
}
#endregion Player Property
}
public class PlayerViewModel : ViewModelBase
{
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
}
}
MainWindow.xaml.cs
using System.Windows;
namespace Player
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
ViewModel.Player = new PlayerViewModel() { Name = "Ivan the Terrible" };
}
// Just here as a convenience, and to make sure we don't give the DataContext
// the wrong kind of viewmodel.
public MainViewModel ViewModel
{
set { DataContext = value; }
get { return DataContext as MainViewModel; }
}
private void Greeting_Click(object sender, RoutedEventArgs e)
{
ViewModel.AddGreeting();
}
}
}
MainWindow.xaml
<Window
x:Class="Player.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:local="clr-namespace:Player"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<WrapPanel>
<Button x:Name="Greeting" Content="Greeting" Click="Greeting_Click" />
<Label>Name: </Label>
<TextBox Text="{Binding Player.Name}" Width="120" />
</WrapPanel>
<ListBox
ItemsSource="{Binding Events}"
DisplayMemberPath="Message"
>
</ListBox>
</StackPanel>
</Grid>
</Window>

You can change the set of Name to be private, but still allow the outside world to read the property with the get.
public string Name { get; private set; } = "Default";
This should give you the functionallity desired without the need to create a new GetName() method.

Related

How to make logs

To explain a little more, I have a Main Form which contains a large list of jobs.
Every item in the list is a instance of my class called Jobs.
When an item is clicked, another Form is opening, in which user can edit information of selected job. I pass a job object from Main Form to details Form and edit it through TextBoxes, ComboBoxes and so on.
Now I need to detect which properties of jobs have changed and write it in log file. I know how to write to log file, but I dont know how to detect which properties have changed.
I could go and write 30 if statements in which I would compare starting point with ending point but I have 30 properties and it would be a complete mess.
Any ideas?
Take a look at INotifyPropertyChanged:https://learn.microsoft.com/en-us/dotnet/framework/winforms/how-to-implement-the-inotifypropertychanged-interface
Example
using log4net;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(Form1).FullName);
public PersonViewPresenter Presenter { get; private set; }
public Form1()
{
InitializeComponent();
Presenter = new PersonViewPresenter();
Presenter.PropertyChanged += Presenter_PropertyChanged;
AddBindings();
}
private void Presenter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Logger.Info($"Property changed {e.PropertyName}");
}
private void AddBindings()
{
_firstnameTextbox.DataBindings.Add(new Binding(nameof(_firstnameTextbox.Text), Presenter, nameof(Presenter.FirstName), false, DataSourceUpdateMode.OnValidation));
_lastnameTextBox.DataBindings.Add(new Binding(nameof(_lastnameTextBox.Text), Presenter, nameof(Presenter.LastName), false, DataSourceUpdateMode.OnValidation));
}
}
}
ViewPresenter implementation
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WindowsFormsApp1
{
public class PersonViewPresenter : ViewPresenterBase
{
private string _lastName;
private string _firstName;
public string FirstName
{
get => _firstName; set
{
if (_firstName != value)
{
_firstName = value;
NotifyPropertyChanged();
}
}
}
public string LastName
{
get => _lastName; set
{
if (_lastName != value)
{
_lastName = value;
NotifyPropertyChanged();
}
}
}
}
public abstract class ViewPresenterBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

UWP MVVM Data Binding for dummies (textbox.text from String)

Well, having a go at MVVM with UWP template 10. I have read many pages, and although everyone tries to say its really easy, I still can't make it work.
To put it into context, OCR is being run on an image, and I would like the text to be displayed in textbox automatically.
Here is my Model:
public class TextProcessing
{
private string _ocrText;
public string OcrText
{
get { return _ocrText; }
set
{
_ocrText = value;
}
}
}
Here is my ViewModel:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TextProcessing _ocrTextVM;
public ScanPageViewModel()
{
_ocrTextVM = new TextProcessing();
}
public TextProcessing OcrTextVM
{
get { return _ocrTextVM; }
set {
_ocrTextVM = value;
this.OnPropertyChanged("OcrTextVM");
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Here is my View:
<TextBox x:Name="rtbOcr"
Text="{Binding OcrTextVM.OcrText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Firstly, that is not working. Could someone try to show where I am going wrong?
Then, the data is coming from a Services file, how would the Services update the value? What would be the correct code?
Thanks in advance.
Following code is cite from code.msdn (How to achieve MVVM design patterns in UWP), it will be helpful for you:
Check you code step by step.
1.ViewModel implemented interface INotifyPropertyChanged,and in property set method invoked PropertyChanged, like this:
public sealed class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ProductName)));
}
}
}
}
2.Initialize you ViewMode in you page, and set DataContext as the ViewMode, like this:
public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();
public MainPage()
{
...
this.DataContext = ViewModel;
}
}
3.In you xaml, binding data from viewMode, like this:
<TextBox Text="{Binding Path=ProductName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="ProductNameTextBox" TextChanged="ProductNameTextBox_TextChanged" />
Your OnPropertyChanged call on OcrTextVM isn't actually called in your case, since you set the value in the constructor to its backing field and bypass the property.
If you set the value via the property, it should work:
public ScanPageViewModel()
{
OcrTextVM = new TextProcessing();
}
Of course your view needs to know that ScanPageViewModel is its DataContext. Easiest way to do it is in the constructor of the code-behind of your view:
public OcrView()
{
DataContext = new ScanPageViewModel();
InitializeComponent();
}
Assuming your OCR service is returning a new TextProcessing object on usage, setting the property of OcrTextVM should suffice:
public class ScanPageViewModel : ViewModelBase, INotifyPropertyChanged
{
//...
private void GetOcrFromService()
{
//...
TextProcessing value = OcrService.Get();
OcrTextVM = value;
}
}
On a note, the OcrTextVM name doesn't really reflect what the property is doing, since it doesn't look like it's a viewmodel. Consider renaming it.
Actually, it is very easy once I manage to understand. Here is the code needed to update a TextBox.Text
In the Models:
public class DisplayText : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
In the XAML file:
<TextBox Text="{Binding Helper.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ... />
In the ViewModels:
private DisplayText _helper = new DisplayText();
public DisplayText Helper
{
get { return _helper; }
set
{
_helper = value;
}
}
Then any mod from the ViewModels:
Helper.Text = "Whatever text, or method returning a string";

How to bind textbox object to ViewModel

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.

wpf databinding: update targets in different views via datacontext

I'm trying to implement a language selection system for my modernui wpf application. When language is switched from the combobox this change would be propagated to all of the application controls by means of databinding.
I've built the skeleton as follows:
languages are objects collected in a List
strings and their counterparts are stored in a static dictionary
data for binding is fetched by a Translation object
views have binding to the datacontext which is based on language framework
Below is the stripped down version of the functionalty and link to the sample vs2013 project. I tried INotify.. without success, I could only manage to update the binding target by resetting datacontext of the view (page1.xaml). Unfortunately couldn't update target on the other view (home.xaml). Question comes down to: "How to update all targets in all views at the same time?"
I'll appreciate any help and suggestions to setup a proper binding structure.
download sample project: http://goo.gl/yjSsKm
home.xaml
<Grid Style="{StaticResource ContentRoot}">
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="{Binding home_text_1}"/>
</StackPanel>
</ScrollViewer>
</Grid>
page1.xaml
<Grid Style="{StaticResource ContentRoot}">
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="{Binding page1_text_1}"/>
<ComboBox x:Name="cbox_lang" Width="120" HorizontalAlignment="Left" Margin="0,50,0,0" SelectionChanged="cbox_lang_SelectionChanged"/>
</StackPanel>
</ScrollViewer>
</Grid>
MainWindow.xaml.cs
using DynamicDataBinding.Pages;
using FirstFloor.ModernUI.Windows.Controls;
namespace DynamicDataBinding
{
public partial class MainWindow : ModernWindow
{
public MainWindow()
{
InitializeComponent();
Language lang_1 = new Language("Language 1");
Language lang_2 = new Language("Language 2");
Global.availableLanguages.Add(lang_1);
Global.availableLanguages.Add(lang_2);
Global.currentLanguage = lang_1;
Global.currentLanguage.set();
}
}
}
page1.xaml.cs
using System.Collections.Generic;
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
foreach (Language lang in Global.availableLanguages)
{
cbox_lang.Items.Add(lang.Name);
}
cbox_lang.SelectedItem = Global.currentLanguage.Name;
this.DataContext = Global.currentLanguage.FrameWork;
}
private void cbox_lang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cbox_lang.SelectedItem.ToString() != Global.currentLanguage.Name)
{
string selectedLanguage = cbox_lang.SelectedItem.ToString();
Global.currentLanguage = Global.availableLanguages.Find(lang => lang.Name == selectedLanguage);
Global.currentLanguage.set();
DataContext = null;
DataContext = Global.currentLanguage.FrameWork;
}
}
}
public class Global
{
public static Dictionary<string, string> dictionary = new Dictionary<string, string>();
public static List<Language> availableLanguages = new List<Language>();
public static Language currentLanguage;
}
public class Language
{
public string Name;
public Translation FrameWork;
public Language(string name)
{
this.Name = name;
}
public void set()
{
Global.dictionary.Clear();
if (Global.currentLanguage.Name == "Language 1")
{
Global.dictionary.Add("home_content_1", "Content For Home in Language 1");
Global.dictionary.Add("page1_content_1", "Content For Page1 in Language 1");
}
else if (Global.currentLanguage.Name == "Language 2")
{
Global.dictionary.Add("home_content_1", "Different Content For Home in Language 2");
Global.dictionary.Add("page1_content_1", "Different Content For Page1 in Language 2");
}
FrameWork = new Translation();
}
}
public class Translation
{
public string home_text_1 { get { return Global.dictionary["home_content_1"]; } }
public string page1_text_1 { get { return Global.dictionary["page1_content_1"]; } }
}
}
home.xaml.cs
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
public partial class Home : UserControl
{
public Home()
{
InitializeComponent();
this.DataContext = Global.currentLanguage.FrameWork;
}
}
}
At the end of the day (eventually) I figured out that there is no need to deal with datacontext. Implementing INotifyPropertyChanged handles all of the target updates if it is setup properly. Furthermore use of a dictionary is not suitable to hold data if binding is in question. Below is the corrected version of the code:
page1.xaml.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
foreach (Language lang in Global.availableLanguages)
{
cbox_lang.Items.Add(lang.Name);
}
cbox_lang.SelectedItem = Global.currentLanguage.Name;
this.DataContext = Global.FrameWork;
}
private void cbox_lang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cbox_lang.SelectedItem.ToString() != Global.currentLanguage.Name)
{
string selectedLanguage = cbox_lang.SelectedItem.ToString();
Global.currentLanguage = Global.availableLanguages.Find(lang => lang.Name == selectedLanguage);
Global.currentLanguage.set();
DataContext = null;
DataContext = Global.FrameWork;
}
}
}
public class Global
{
public static Dictionary<string, string> dictionary = new Dictionary<string, string>();
public static List<Language> availableLanguages = new List<Language>();
public static Language currentLanguage;
public static Translation FrameWork = new Translation();
}
public class Language
{
public string Name;
public Language(string name)
{
this.Name = name;
}
public void set()
{
Global.dictionary.Clear();
if (Global.currentLanguage.Name == "Language 1")
{
Global.FrameWork.home_text_1 = "Content For Home in Language 1";
Global.FrameWork.page1_text_1 = "Content For Home in Language 1";
}
else if (Global.currentLanguage.Name == "Language 2")
{
Global.FrameWork.home_text_1 = "Different Content For Home in Language 2";
Global.FrameWork.page1_text_1 = "Different Content For Home in Language 2";
}
}
}
public class Translation : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _home_text_1;
private string _page1_text_1;
public string home_text_1 { get {return _home_text_1;} set { _home_text_1 = value; OnPropertyChanged("home_text_1"); }}
public string page1_text_1 { get {return _page1_text_1;} set { _page1_text_1 = value; OnPropertyChanged("page1_text_1");}}
}
}

Two Way Binding in WPF

I am (very) new to WPF and I have got a question regarding that. It may be a very stupid one, so please excuse me if that is the case.
I am doing a project where I am binding my textboxes, etc to static properties inside a singleton class. My problem is that the twoWay Binding is not working. When the textbox changes, I can see that the value of the property changes, but when the property changes, I can't see the textbox text changing.
To see what's going on I wrote a small app, with just the relevant code. Please find the code below.
In the code below, I am changing the text in the textbox and source property in various places and noted my observations. If someone can tell me what I am doing wrong and point me in the right direction, I would be very grateful.
I also tried INotifyPropertyChanged, but it gives problems because of the static property. Is there a different approach when implementing INotifyPropertyChanged for a static property.
Thanks in advance,
Abhi.
XAML:
<Page x:Class="TestBindingApp.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Prefs="clr-namespace:TestBindingApp"
xmlns:cm="clr-namespace:System.ComponentModel;assembly=System"
xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
Title="Page1" Loaded="Page_Loaded">
<Page.Resources>
<Prefs:Class1 x:Key="TClass"></Prefs:Class1>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="15 5 5 0" Height="20">
<TextBlock Name="txbBankNumber" Margin="50 0 0 0" Padding="2">Bank Account Number :</TextBlock>
<TextBox Name="txtBankNumber" Margin="10 0 0 0" Width="100" MaxLength="8" HorizontalAlignment="Left">
<TextBox.Text>
<Binding Source="{StaticResource TClass}" Path="AccountNumber" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
</Grid>
XAML.CS:
namespace TestBindingApp
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
txtBankNumber.Text = "ABC";
// I can see the property AccountNumber changing here
Class1.AccountNumber = "123456";
// Value in txtBankNumber doesn't change here
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
txtBankNumber.Text = "ABCDE";
// I can see the property AccountNumber changing here
Class1.AccountNumber = "12345678";
// Value in txtBankNumber doesn't change here
}
}
}
Class Class1:
using System.ComponentModel;
namespace TestBindingApp
{
public class Class1
{
// Singleton instance
private static Class1 instance;
private static string _accountNumber;
public Class1()
{
}
// Singleton instance read-only property
public static Class1 Instance
{
get
{
if (instance == null)
{
instance = new Class1();
}
return instance;
}
}
public static string AccountNumber
{
get
{
return _accountNumber;
}
set
{
if (value != _accountNumber)
{
_accountNumber = value;
}
}
}
}
}
=====================
Couldn't post my updated code in the comments, so updating my original post.
Below is my updated code, which has the "if(PropertyChanged != null)", but it gives me an error - "An object reference is required for the non-static field, method, or property 'TestBindingApp.Class1.NotifyPropertyChanged(string)'". .
I have just started learning WPF, so if you could explain in detail, that would be very helpful. Thanks for your patience.
using System.ComponentModel;
namespace TestBindingApp
{
public class Class1: INotifyPropertyChanged
{
// Singleton instance
private static Class1 instance;
private static string _accountNumber;
public event PropertyChangedEventHandler PropertyChanged;
public Class1()
{
}
// Singleton instance read-only property
public static Class1 Instance
{
get
{
if (instance == null)
{
instance = new Class1();
}
return instance;
}
}
public static string AccountNumber
{
get
{
return _accountNumber;
}
set
{
if (value != _accountNumber)
{
_accountNumber = value;
NotifyPropertyChanged("AccountNumber");
}
}
}
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
==============
Updated 23rd June, 09:53 AM UK time
Hi Arcturus, I have changed the properties to non-static, but it is still not behaving as I expect it to. Am I expecting it to do something which it isn't meant to do, or am I doing something wrong.
In the below code, I expected the textbox to show 12345678 (or maybe 123456) as the account number, but it still shows 123. In the debug mode, I can see PropertyChanged event executing correctly after each property change statement, but the value of the textbox doesn't change. Does the binding take affect only at the time of initialization (InitializeComponent()), or am I missing something here?
Page code-behind
namespace TestBindingApp
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
Class1.Instance.AccountNumber = "123";
InitializeComponent();
Class1.Instance.AccountNumber = "123456";
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
Class1.Instance.AccountNumber = "12345678";
}
}
}
Class1.cs
namespace TestBindingApp
{
public class Class1: INotifyPropertyChanged
{
// Singleton instance
private static Class1 instance;
private static string _accountNumber;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Class1()
{
}
// Singleton instance read-only property
public static Class1 Instance
{
get
{
if (instance == null)
{
instance = new Class1();
}
return instance;
}
}
public string AccountNumber
{
get
{
return _accountNumber;
}
set
{
if (value != _accountNumber)
{
_accountNumber = value;
OnPropertyChanged("AccountNumber");
}
}
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
You really need the INotifyPropertyChanged interface:
using System.ComponentModel;
namespace TestBindingApp
{
public class Class1
{
// Singleton instance
private static Class1 instance;
private string _accountNumber;
public Class1()
{
}
// Singleton instance read-only property
public static Class1 Instance
{
get
{
if (instance == null)
{
instance = new Class1();
}
return instance;
}
}
public string AccountNumber
{
get
{
return _accountNumber;
}
set
{
if (value != _accountNumber)
{
_accountNumber = value;
NotifyPropertyChanged("AccountNumber");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
You call NotifyPropertyChanged from a static member, but NotifyPropertyChanged itself isn't static.
Two ways to solve: Either make AccountNumber NOT static or provide an instance for your call to NotifyPropertyChanged (e.g. "Instance.NotifyPropertyChanged(...)")

Categories

Resources