In my MainWindow I create a new instance of a class containing different settings. After setting the parameters of the class, I set the datacontext = to that class.
public partial class MainWindow : Window
{
private MeasConSettings mMeasConSettings = new MeasConSettings();
public MainWindow()
{
InitializeComponent();
DataContext = mMeasConSettings;
}
private void MenuComm_Click(object sender, RoutedEventArgs e)
{// See code below}
}
Now I also have a function to open a new window, this window contains a textbox who's text should be bound to the datacontext of the MainWindow.
private void MenuComm_Click(object sender, RoutedEventArgs e)
{
FrmSettings newWindow = new FrmSettings();
newWindow.DataContext = mMeasConSettings;
newWindow.TxtComm.Text = mMeasConSettings.CommSettings;
newWindow.Show();
}
This code fills in the textbox from the newWindow with the right content, BUT it does not get bound propery since the datacontext does not get updated after changing the text in the textbox (TxtComm in the new created window).
An example of the XAML code for the textbox:
<TextBox Grid.Row="1" Grid.Column="3" Margin="2,0" Name="TxtComm" DataContext="{Binding Path=CommSettings, UpdateSourceTrigger=PropertyChanged}" />
"CommSettings" is a member of the MeasConsettings class
public class MeasConSettings
{
private string mCommSettings;
public string CommSettings
{
get
{
return mCommSettings;
}
set
{
mCommSettings = value;
}
}
public MeasConSettings()
{
CommSettings = "Com5:19200,8,n,1";
}
}
My problem is how can I adjust the value mMeasConSettings.CommSettings (defined in my MainWindow) in my newWindow (Which is created after pressing a button), If I change the textbox value in my newWindow, the value stored in mMeasConSettings.CommSettings should also be changed.
PS: I'm new to WPF so any advice is welcome!
As I wrote in the comment, you need to bind the Text property of your TextBox to the property of the DataContext which you want to update. Your XAML should thus be something like:
<TextBox ... Text="{Binding CommSettings, Mode=TwoWay}" />
Note that I am binding the Text property of the TextBox to the property CommSettings of your DataContext. And your C#-code for the click event should be:
private void MenuComm_Click(object sender, RoutedEventArgs e)
{
FrmSettings newWindow = new FrmSettings();
newWindow.DataContext = mMeasConSettings;
newWindow.Show();
}
We only need to set the DataContext here. Note that the DataContext is passed along to child elements, so the TextBox will have the same DataContext as its parent unless specifically set to something else.
use static property:
class Demo
{
public static string SomeSettings {get;set;}
private onLoad()
{
SomeSettings=... //Init here
}
}
In other file:
Demo.SomeSettings=....
Related
I'm still relatively new to Data Binding in wpf, but despite plunging through all the articles and posts here and elsewhere about what could be wrong, I still have not found a solution. The code below is the prevalent information parsed out of my larger files.
I have made sure everything else is working, including adding a property to retrieve the protected parameter options to ensure options.FullPath is in fact getting set/changed on the Browse button's Click event. I attempted to subscribe to the PropertyChanged event in the main window with the line test.PropertyChanged += ShowMessage;, ShowMessage being a method that triggers a MessageBox with text in it. I tried multiple variations on the OnPropertyChanged method from hardcoding it within the calling method to what is displayed here. I even tried setting options to a default value of "" just in case it was being weird about that. No luck on anything, and I have no way to acquire C#6 at the moment, so it may very well be that what I have works with the right language updates, but I just can't tell since it doesn't trigger.
Any help or insight would be greatly appreciated!
EDIT: All of the below code is house within the same namespace.
Object Class:
public class EEOptionSet: INotifyPropertyChanged
{
public EEOptionSet()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private string _fullPath;
public string FullPath
{
get { return _fullPath; }
set
{
if (value != _fullPath)
{
_fullPath = value;
OnPropertyChanged();
}
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Main window's code behind:
public partial class window : Window
{
protected EEOptionSet options = new EEOptionSet();
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
Textbox and Button instances in the xaml of my main window (extraneous properties like Grid placement, Alignment, etc removed for brevity):
<TextBox x:Name="FullPathText" Text="{Binding (options.FullPath), Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" Focusable="False"/>
<uc:ButtonExt x:Name="Browse" Content="..." Click="BrowseFiles"/>
NOTE: I have also tried:
Text="{Binding options.FullPath, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=options.FullPath, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=(_currentOptionSet.FullPath), Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
As well as without the IsReadOnly and Focusable properties.
You cannot bind to a protected field.
Set the DataContext of the window to your field:
public partial class window : Window
{
protected OptionSet options = new OptionSet();
public window()
{
InitializeComponent();
DataContext = options;
}
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
...and remove "options" from the binding path(s) in the XAML markup:
Text="{Binding FullPath, UpdateSourceTrigger=PropertyChanged}"
Alternatively, make options a public property of the window and set the DataContext the the window itself:
public partial class window : Window
{
public OptionSet options { get; private set; }
public window()
{
InitializeComponent();
options = = new OptionSet();
DataContext = this;
}
private void BrowseFiles(object sender, RoutedEventArgs e)
{
options.FullPath = "Test";
}
}
Then you should keep the binding path as-is:
Text="{Binding options.FullPath, UpdateSourceTrigger=PropertyChanged}"
PropertyPath (this is the type of the Binding.Path property) can only be set by the path expression to the public property of the source. And your variable options is a protected field.
If the source is not explicitly specified in the Binding (there are three ways of setting it: Source, ElementName and RelativeSource), then the Data Context of the element in which the binding is set is used for the source. You did not specify the source in any of these four ways.
An example of setting the Data Context and its use.
Written from assumption:
the EEOptionSet and the OptionSet classes - are one and the same, and you just made a mistake when copying the code;
the EEOptionSet class is declared in the same namespace as your window.
<Window.DataContext>
<local:EEOptionSet/>
<Window.DataContext>
protected readonly EEOptionSet options;
public window()
{
InitializeComponent();
options = (EEOptionSet) DataContext;
}
<TextBox Text="{Binding FullPath, Mode=OneWay}"
IsReadOnly="True"
Focusable="False"/>
I am new to WPF and I have created a WPF Application. In that application, I have a UserControl that contain a button as below,
<UserControl.DataContext>
<local:AppViewModel/>
</UserControl.DataContext>
<Grid>
<Button x:Name="Btn_Contact" Command="{Binding BookVM.LoadContactsCommand}" Click="Btn_Contact_Click"/>
</Grid>
And My AppViewModel Class is as below
public AppViewModel()
{
var dataService = new JsonContactDataService();
BookVM = new BookViewModel(dataService);
CurrentView = BookVM;
}
My problem is I want this UserControl to run command of the Btn_Contact automatically when the UserControl is loaded instead of clicking the button. I have try to write the command binding on the UserControl code-behind but it does not worked.
public UserControlMemo()
{
InitializeComponent();
Btn_Contact.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
Btn_Contact.SetBinding(Button.CommandProperty, new Binding("BookVM.LoadContactsCommand"));
}
In its simplest form:
public UserControlMemo()
{
Loaded += OnLoaded;
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is AppViewModel viewModel)
viewModel.BookVM.LoadContactsCommand.Execute(null);
}
I'm trying to learn WPF MVVM I would need to understand how to update a textbox value via a modal window. Below the code, I wrote passes the value to the viewmodel but does not update the textbox. Thanks in advance
UserControl con il TextBox
<TextBox x:Name="Text01UC" Text="{Binding TextUC, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="200" Height="33"/>
UserControl Behind
namespace InvioDati
{
public partial class textbox : UserControl
{
public textbox()
{
InitializeComponent();
var vm = new ModelTextView();
this.DataContext = vm;
vm.Load();
}
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.ShowDialog();
}
}
}
ModelTextView
namespace InvioDati
{
class ModelTextView : BaseViewModel
{
private ModelText dati = new ModelText();
public string TextUC
{
get => dati.TextVal;
set
{
dati.TextVal = value;
OnPropertyChanged();
}
}
public void Load() {
TextUC = "GoodMorning";
}
public void Ricevi(string valore)
{
TextUC = valore;
}
}
}
ModalWindow Code behind
namespace InvioDati
{
public partial class MoadalWindow : Window
{
public MoadalWindow()
{
InitializeComponent();
}
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = new ModelTextView();
nd.Ricevi(Send.Text);
this.Close();
}
}
}
Set the DataContext of the ModalWindow to the same instance of ModelTextView in textbox.xaml.cs:
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.DataContext = this.DataContext;
md.ShowDialog();
}
You can then either bind directly to the TextUC property or do the following in ModalWindow.xaml.cs:
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = DataContext as ModelTextView;
nd.Ricevi(Send.Text);
this.Close();
}
You must use a mediator in order not to break mvvm here.
Check https://en.wikipedia.org/wiki/Mediator_pattern#C#
1b. Add Observer pattern to create notifications for value changes.
Dialogs are evil within MVVM, usually you won't need them. What you want is an overlaying View, which can be Data bound in any way as there is no break in the visual tree
If you want to use "dialogs", implement a DialogService to do so.
Edit: here is a draft on how you create something "popup" like in the most simple way:
<UserControl>
<Grid>
<!--Invert visability of all controls below via binding-->
<YourMainControl/>
<Rect Fill="Black" Opacity=".5 Visibility="Hidden"/>
<YourSubControl Visibility="Hidden"/>
</Grid>
</UserControl>
There are a number of similar queries on the internet from which I have written the following code. But it does not seem to be working.
There are two windows: Mainwindow and Window1.
Mainwindow has a textbox named RUNTIME
Window1 has a textbox STOPTIME.
When a button on mainwindow is pressed window1 opens.
I want to create a binding between the two text boxes such that if I write something in RUNTIME and open window 1, stop time should have the same value. And when I open window1 and write something in STOPTIME it should immediately be reflected in RUNTIME.
Please note that on the main window there is no enter button. Just as anything is written in textbox RUNTIME, it is saved in a variable Time.
Main Window
<Window>
.
.
.
<TextBox x:Name="RUNTIME" Width="120" Text="{Binding runtime,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<MenuItem Header="Menu">
<MenuItem Header="SubMenu" Click="set_config_param" StaysOpenOnClick="True"/>
</MenuItem>
.
.
.
</Window>
Window1
<Window>
.
.
.
<TextBox x:Name="STOPTIME" HorizontalAlignment="Left" Height="23" Margin="145,27,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding runtime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
.
.
.
</Window>
The Time variable is used to store runtime/stoptime. I have created a separate class for this.
public class Time : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private string _runtime;
private void OnPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public string runtime
{
get
{
return _runtime;
}
set
{
_runtime = value;
OnPropertyChanged("runtime");
}
}
}
Now the C# code.
Main Window
public partial class MainWindow : Window
{
Time _Time = new Time();
public MainWindow()
{
InitializeComponent();
RUNTIME.DataContext = _Time;
}
private void set_config_param(object sender, RoutedEventArgs e)
{
bool isWindowOpen=false;
foreach (Window w in Application.current.Windows)
{
if (w is Window1)
{
isWindowOpen = true;
w.Activate();
}
if (!isWindowOpen)
{
Window1 newwindow = new Window();
newwindow.Show();
}
}}
}
Window1
public partial class Window1 : Window
{
Time _Time = new Time();
public Window1()
{
InitializeComponent();
STOPTIME.DataContext = _Time;
}
private void OK_Window1_Button_Click(object sender, RoutedEventArgs e)
{
_Time.runtime = STOPTIME.Text;
}
}
The problem I have understood so far is that I have failed to instantiate the same instance of time in both the forms. How can I do that? Where exactly should i change my code.
It looks to me that you are doing it wrong. Since your goal is to share same object across different windows, it must be stored somewhere, where each of those windows can access. So the easiest way to implement this is using Singleton design pattern.
You can bind to static class as following:
...Text={Binding Path=<Path_on_the_Singleton_object>, Source={x:Static namespace:StaticClass.<Static_Singleton_Property>}}...
Copying a comment from # jayars:
in your MainWindow, you are creating a new instance of _Time. Next in Window1, you're creating a new instance of _Time again. Try modifying public Window1() to public Window1(Time _Time), and inject the same instance via Window1 newwindow = new Window1(this._Time)
I have a UserControl called LoginControl where I have defined a Command:
//This is LoginControl
public ICommand LoginCommand
{
get { return (ICommand)GetValue(LoginCommandProperty); }
set { SetValue(LoginCommandProperty, value); }
}
public static readonly DependencyProperty LoginCommandProperty =
DependencyProperty.Register("LoginCommand",
typeof(ICommand),
typeof(LoginControl));
I have a button in the LoginControl where the Click event calls this event handler:
private void Login_Click(object sender, RoutedEventArgs e)
{
LoginCommand.Execute(passwordBox.Password);
}
Now I have another UserControl called SettingsControl where I have included the LoginControl:
<local:LoginControl Grid.Row="1" Margin="5" LoginCommand="{Binding MyCommand}"/>
The DataContext of the SettingsControl is set to itself : DataContext="{Binding RelativeSource={RelativeSource Self}}
SettingsControl is defined like this:
public partial class SettingsControl : UserControl
{
public SettingsControl()
{
InitializeComponent();
MyCommand = new RelayCommand(o => MessageBox.Show("YESSS!"));
}
public ICommand MyCommand { get; set; }
}
When the Login_Click event handler is called, the code throws a NullReferenceException (MyCommand is null). I don't understand why. The MyCommand is initialised as you can see here. When I am initialising the LoginControl, I pass the MyCommand to it. So I don't understand why it should be null.
After like 2 hours thinking, I have found the solution.
Move MyCommand initialisation above InitializeComponent (Thanks to #Spawn for the suggestion)
Give a name to SettingsControl in xaml. Then change the LoginCommand binding as follows:
{Binding MyCommand, ElementName=settingControl}
And this worked. I am guessing the previous binding was looking for the DataContext of the LoginControl itself. By specifying the ElementName, wpf knows what DataContext I wanted (i.e. the DataContext of the SettingsControl).