Data binding between two textboxes in different windows using WPF - c#

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)

Related

WPF PropertyChanged event not firing/updating textbox

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"/>

How do I access the content of the frame in different class? WPF

I wanted to change the frame's content to the Register.xaml page when the RegisterButton is click but it doesn't do it. How do I properly access the frame from the MainWindow?
Please help! I've been here long enough.
MainWindow.xaml
<Frame x:Name="MainFrame" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3"
Padding="10" BorderBrush="DarkGray" BorderThickness="2"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainFrame.Content = new Navigation();
}
public void GoToRegister()
{
MainFrame.Content = new Register();
}
}
Navigation.xaml
<Button x:Name="RegisterButton" Grid.Row="0" Grid.Column="0" Background="LightSteelBlue" Content="Register" FontSize="24" Margin="10" Click="RegisterButton_Click"/>
Navigation.xaml.cs
public partial class Navigation : Page
{
public Navigation()
{
InitializeComponent();
}
private void RegisterButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("success");
MainWindow Main = new MainWindow();
Main.MainFrame.Content = new Register();
}
}
In your Navigation you're creating a new MainWindow (which you don't display) and this is why it's not working. If you want to access MainWindow from underlying classes (I wouldn't do it, but it's a different story), you can, for example, create a public static method that takes a Page as an input parameter and sets it as the content for MainFrame:
In your MainWindow.xaml.cs
public static void SetMainFrameContent(Page page)
{
this.MainFrame.Content = page;
}
And then in your Navigation.xaml.cs button click event handler you call this method:
private void RegisterButton_Click(object sender, RoutedEventArgs e)
{
MainWindow.SetMainFrameContent(new Register());
MessageBox.Show("success");
}
What also may help is setting the data context of each of the windows, simply call this.DataContext = this; in the constructor.

Raise a event on the User control from the Main Window in WPF

There is a textbox in my mainwindow.xaml, when I enter the textbox, I expect the label in my usercontrol, known as View1.xaml will be update accordingly. However I realise the event is not raise at all in the user control when I type the textbox, can you tell me which part is wrong?
The event is able to raise in TextBox_TextChanged_1
my MainWindow.XAML
<Window xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:testapplication" x:Class="testapplication.MainWindow"
Title="MainWindow" Height="964" Width="790">
<Grid >
<Button x:Name="OpenView1" Content="Open Window 1" HorizontalAlignment="Left" Margin="33,70,0,0" VerticalAlignment="Top" Width="111" RenderTransformOrigin="0.279,1.409" Click="OpenView1_Click"/>
<Button x:Name="OpenView2" Content="Open Window 2" HorizontalAlignment="Left" Margin="33,169,0,0" VerticalAlignment="Top" Width="111" Click="OpenView2_Click"/>
<Button x:Name="OpenView3" Content="Open Window 3" HorizontalAlignment="Left" Margin="33,259,0,0" VerticalAlignment="Top" Width="111" Click="OpenView3_Click"/>
<local:View1 x:Name="ViewOne" HorizontalAlignment="Left" Margin="33,332,0,0" VerticalAlignment="Top" Height="226" Width="204" Visibility="Hidden"/>
<local:View2 x:Name="ViewTwo" HorizontalAlignment="Left" Margin="284,332,0,0" VerticalAlignment="Top" Height="226" Width="208" Visibility="Hidden"/>
<local:View3 x:Name="ViewThree" HorizontalAlignment="Left" Margin="534,332,0,0" VerticalAlignment="Top" Height="226" Width="196" Visibility="Hidden"/>
<TextBox HorizontalAlignment="Left" Height="42" Margin="326,70,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="182" FontSize="22" TextChanged="TextBox_TextChanged_1"/>
</Grid>
</Window>
my MainWindow.cs
namespace testapplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
//InitializeComponent();
}
//event handler
public event EventHandler<EventArgs> changedText;
private void OpenView1_Click(object sender, RoutedEventArgs e)
{
ViewOne.Visibility = Visibility.Visible;
}
private void OpenView2_Click(object sender, RoutedEventArgs e)
{
ViewTwo.Visibility = Visibility.Visible;
}
private void OpenView3_Click(object sender, RoutedEventArgs e)
{
ViewThree.Visibility = Visibility.Visible;
}
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
if (changedText != null)
{
changedText(this, e);
}
}
}
}
This is my UserControl, known as View1.xaml, it is included in my MainWindow.Xaml
namespace testapplication
{
/// <summary>
/// Interaction logic for View1.xaml
/// </summary>
public partial class View1 : UserControl
{
private MainWindow newWindow = new MainWindow();
public View1()
{
InitializeComponent();
newWindow.changedText += newWindow_ChangeText;
}
void newWindow_ChangeText(object sender, EventArgs e)
{
ViewOnelabel.Content = "Happy";
}
}
}
The problem is my ViewOnelabel.Content = "Happy" did not execute at all, it remain unchanged
There are a few things I would like to point out.
The equivalent of a winforms label in wpf is a TextBlock. A wpf label is actually a type of contentcontrol. Hence the content property.
In wpf there are routed events. These "bubble" up ( and tunnel down ) the visual tree. That means you can handle an event in the window from a control in a usercontrol inside it.
But mainly.
I encourage you to look into the MVVM pattern.
I've put together some code which illustrates these points.
I'd recommend just using binding and mvvm though.
My MainWindow markup:
Title="MainWindow" Height="350" Width="525"
TextBoxBase.TextChanged="Window_TextChanged"
>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<Label Name="OutputLabel"/>
<TextBlock Text="{Binding OutputString}"/>
<local:UserControl1/>
</StackPanel>
</Grid>
Notice that it handles a textchanged event and because that's routing it will get the event from UserControl1 inside it.
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_TextChanged(object sender, TextChangedEventArgs e)
{
OutputLabel.Content = $"Happy {((TextBox)e.OriginalSource).Text}";
}
}
You don't do anything with the text from your textbox in your handler but I have some code there proves you could get at that from mainwindow if you wanted.
My viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private string inputString;
public string InputString
{
get { return inputString; }
set
{
inputString = value;
OutputString = $"{inputString.Length} characters entered";
RaisePropertyChanged();
}
}
private string outputString;
public string OutputString
{
get { return outputString; }
set
{
outputString = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Usercontrol1 just has a textbox:
<Grid>
<TextBox Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
As you type in that textbox, the text is transferred to the bound property in my viewmodel. That hits the code in my setter. This in turn sets OutputString which is bound to my textblock.
Text changes in both my label and textblock as I type.
Here's a link to my sample on onedrive
https://1drv.ms/u/s!AmPvL3r385QhgpgOPNKPs-veFJ2O3g
The main problem here is that your View1 class is subscribing to an event on a new MainWindow instance, not the MainWindow instance created by your application on start.
Since your MainWindow class has a reference to your View1 class (a named member "ViewOne") you should just change it from the MainWindow class.
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
ViewOne.ViewOneLabel.Content = "Happy";
}
Get rid of the chenagedText event handler and all the code in the View1.xaml.cs... you don't need it.
Note: I am hoping that you are just playing around and learning here... there is no way I would condone building a WPF application in this way.
You could only use the event of the MainPage. I recomment you to add a Property to the UserControl. In my case I call it Text.
public string Text
{
set { ViewOneLabel.Content = value; }
}
In the MainWindow use the Property within the TextChanged Event.
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
OpenView1.Text = TextBox.Text;
}
You are creating a new instance of MainWindow in your UserControl. What you want to do is to hook up an event handler to the instance that you actually see on the screen. You can get a reference to this one using the Window.GetWindow method:
public partial class View1 : UserControl
{
public View1()
{
InitializeComponent();
Loaded += (s, e) =>
{
Window mainWindow = Window.GetWindow(this) as MainWindow;
if(mainWindow != null)
mainWindow.changedText += newWindow_ChangeText;
};
}
void newWindow_ChangeText(object sender, EventArgs e)
{
ViewOnelabel.Content = "Happy";
}
}

Databinding and then calling from another class in WPF

My purpose is to add a textblock to my main UI window, of which text will be updated if needed. For that, in my UIWindow xaml I did like this:
<Window x:Class="UIDesigner.UIWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:UIDesigner"
xmlns:c="clr-namespace:UIDesigner.Controls"
WindowStartupLocation="CenterScreen"
WindowState="Maximized"
WindowStyle="SingleBorderWindow"
Title="GUI"
Height="1000" Width="1400"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Icon="Resources/Images/Logo.png"
>
<Grid Margin="0">
<Grid Grid.Row="1" Margin="0,10,0,0">
<GroupBox Header="Console" Grid.Column="1" Margin="0,590,0,0" HorizontalAlignment="Stretch" x:Name="consoleWindow" IsEnabled="True" VerticalAlignment="Stretch"
>
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=consoleText}"/>
</GroupBox>
</Grid>
</Grid>
</Window>
This is the code behind:
using System.Windows;
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace UIDesigner
{
public partial class UIWindow : Window
{
public UIWindow()
{
InitializeComponent();
}
private string _consoleText;
public string consoleText
{
get{ return _consoleText;}
set
{
_consoleText = value;
NotifyPropertyChanged("consoleText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Then in my main class, I call this UIWindow like this:
namespace UIDesigner
{
public partial class Main : Window
{
public Main()
{
InitializeComponent();
}
private void LoginButton_Click_1(object sender, RoutedEventArgs e)
{
var myUIWindow = new UIWindow();
myUIWindow.PropertyChanged += new PropertyChangedEventHandler(UIWindow_PropertyChanged);
myUIWindow.consoleText = "Hello User!";
myUIWindow.ShowDialog();
this.Close();
}
private void LoginButton_MouseEnter_1(object sender, MouseEventArgs e)
{
}
static void UIWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("Something Changed!");
MessageBox.Show(e.PropertyName);
}
}
}
Now I have two problems here:
First, when my UI window starts, I indeed received two message boxes, saying "something changed" followed by "consoleText". So that means the consoleText is changed successfully. But after my UIWindow shows up, the textblock is empty, I cannot see "Hello User!" there. Seems like Text="{Binding Path=consoleText} part is not working correctly in my xaml file.
Second and most importantly, I want to change the consoleText in another different class, namely in DesignerCanvas.Commands.cs. For that I couldn't figure out any solution. I want something like this in my DesignerCanvas.Commands.cs:
namespace UIDesigner
{
public partial class DesignerCanvas
{
private void changeConsoleOutput(string updatedConsoleText)
{
myUIWindow.consoleText = updatedConsoleText; //obviously, this is not working
}
}
}
Any kind of suggestion will be much appreciated.
1.First of two set the value in UI just add below one line
in constructor of UIWindow class
this.DataContext=this;
//because only specifying property consoletext, it will not able to know where to find consoletext.
2.u can find that UIwindow in App.Current.Windows and cast it to UIWindow type and then can
access the property.
foreach(Window win in App.Current.Windows)
{
if (win as UIWindow != null)
{
(win as UIWindow).consoletext = updatedConsoleText;
}
}
For second problem
Change
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=consoleText}"/
To
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=.}"/
and
in UIWindow constructor set
myConsoleWindowTextBlock.Datacontext=consoleText;

Access the main window datacontext from a new created window

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=....

Categories