Data Retain After Changing DataContext/ Views in WPF - c#

I am new to WPF and want to build an application which will do serial communication with the my driver and from program i can set values to the driver.
I have managed to make a UI as shown in this figure here . If i press Blue View as pointed by the arrow at last, the view of my window is like this. If i press the Red View Option then the display is like this
Setting button is where the arrow pointing at top right corner(below the close button of window) and when pressed my window will look like this.
Basically i am changing the BIG RECTANGLE content according to the button i have pressed for example (rectangle is blue when Blue view is clicked, rectangle filled up with Red and one label and a button to change the label when Red View is clicked)
So now my problem is I cannot retain the value i had set in this BIG RECTANGLE after i change the content of this BIG RECTANGLE. For example when i pressed the setting button and change the setting like this I am ready for communication in COM5 and the option to close the port can be pressed. Now before closing the port if I change the view of the BIG RECTANGLE by pressing Red View or Blue View then after press the setting button then i don't have that option to close port anymore and since I had already opened the com5 port earlier so when i try to open the port it will also give me error.
Please Help me with this. My visual studio solution explorer looks like this and My code in the button clicked event are as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SerialPortOnOFFButton_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("ON OFF Clicked");
}
private void SerialPortSettingButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new SerialPortSettingView();
}
private void RedViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new Redview();
}
private void BlueViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = new Blueview();
}
}
And my Solution explorer looks like this

If you want to just close your port you can implement IDisposable interface in your SerialPortSettingView class like this. Each time you change DataContext from SerialPortSettingView your port will be closed.
public class SerialPortSettingView : IDisposable
{
private FileStream _fileStream;
public SerialPortSettingView()
{
_fileStream = new FileStream("somefile.txt", FileMode.Open);
}
public void Dispose()
{
_fileStream?.Close();
}
}

Each time you change the view you are creating a new object to set the DataContext to, if you kept the individual objects as private fields you could then simply set the DataContext to these:
private SerialPortSettingView _serialPortSettingView;
private RedView _redView;
private BlueView _blueView;
public MainWindow()
{
_serialPortSettingView = new SerialPortSettingView();
_redView = new RedView();
_blueView = new BlueView();
InitializeComponent();
}
private void SerialPortOnOFFButton_Clicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("ON OFF Clicked");
}
private void SerialPortSettingButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _serialPortSettingView;
}
private void RedViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _redview;
}
private void BlueViewButton_Clicked(object sender, RoutedEventArgs e)
{
DataContext = _blueview;
}
This way when you switch between views you'll be using the stored version and when you change values they will be stored in that View.
If I were doing this solution I would change the ContentControl to a TabControl (hide the headers) then create each View, with an accompanying ViewModel, as tabs. Then as each click event is fired you simply set the .SelectedIndex property of the TabControl. I'd create a MainWindowViewModel and set the MainWindow's DataContext to this in the constructor:
private MainWindowViewModel = new MainWindowViewModel();
public MainWindow()
{
DataContext = _mainWindowViewModel;
}
And put all the logic in the MainWindowViewModel (you need to use commands). Using code behind isn't what WPF was meant for, you can read up on it all here with a good tutorial to follow - MVVM Tutorial
I don't want to over complicate things and stuff you with too much information in one go but if you start off doing things this way it will be better, hope this helps.

Related

What is the previous window called in WPF

I know the current window can be used with "this" but is there anything I can use to call the previous window?
For example I have this code going off when I press a button
Buyer_Login BuyerWindow = new Buyer_Login();
Visibility= Visibility.Hidden;
BuyerWindow.Show();
I need to be able to go back to the first window and I need to close the BuyerWindow and I was going to do it with this.Close();
What can I do to make the first window's visibility visible again?
You could handle the Window.Closed event:
MainWindow.xaml.cs
private void OnClick(object sender, EventArgs e)
{
var loginWindow = new BuyerLogin();
loginWindow.Closed += OnBuyerLoginWindowClosed;
this.Visibility = Visibility.Hidden;
loginWindow.Show();
}
private void OnBuyerLoginWindowClosed(object sender, EventArgs e)
=> this.Visibility = Visibility.Visible;
You should consider to show the login window from the App.xaml.cs before you show your main window (recommended):
App.xaml.cs
private async void App_OnStartup(object sender, StartupEventArgs e)
{
var loginWindow = new BuyerLogin();
bool? dialogResult = loginWindow.ShowDialog();
if (dialogResult.GetValueOrDefault())
{
var mainWindow = new MainWindow();
mainWindow.Show();
}
}
App.xaml
<Application Startup="App_OnStartup">
<Application.Resources>
</Application.Resources>
</Application>
There is a collection of open windows.
App.Current.Windows;
It depends on exactly what you're doing opening windows.
If you start up then mainwindow will be [0] in that collection.
Say you then open an instance of window1.
That in turn opens an instance of window2.
There is a bit of a complication if you f5 in visual studio because it opens adorner windows.
Setting that aside for a moment.
When I write code to do what I describe above.
In Window2 I handle content rendered:
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
var wins = App.Current.Windows;
wins[1].Close();
}
}
That instance of Window1 is closed.
Your new window is very likely the last window in that zero based collection and the previous one the window before that.
You could perhaps search the collection and find index for "this" and subtract one if you're doing more complicated things.
The chances are though, you want to close the window indexed by the count of that collection minus 2. Because it's zero based.
With my exploratory code, window1 closes with this:
private void Window_ContentRendered(object sender, EventArgs e)
{
var wins = App.Current.Windows;
wins[wins.Count - 2].Close();
}
Personally, I prefer single window apps and switch out the content in part of mainwindow. Leaving navigation buttons etc static in mainwindow.
If you're effectively opening one other window and closing the previous to do things then maybe you could consider a single window app instead.

Winform copy button text to textbox using universal method

So this is a fairly straightforward thing, and I am just curious if there is a better way to do it to save lines of code. For class we are making a teletype machine. Basically there is a textbox, and a series of buttons A-Z and 0-9. When you click the button it adds the corresponding letter/number to the textbox. When you click send, it adds the contents of the textbox to a label and resets the textbox. Everything works and it only took a few minutes to build. However there is a mess of redundant lines and I was curious if there is a way to clean up the code with a method.
This is my current code.
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = box_UserInput.Text + "A";
}
As you can see, it is very simplistic and straight forward. Click A, and "A" gets added to the textbox. However the Text property of the button is also just "A" and I want to know if there is a way to just copy the text property of that button and add it to the textbox string.
Something like this, except with a universal approach where instead of having to specify btn_A it just inherits which button to copy based on the button clicked. That way I can use the same line of code on every button.
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = box_UserInput.Text + btn_A.Text;
}
You can use this which is more universal as the Control class contains the Text property. Also, using the best practice $"".
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";
}
You can also assign the same event to each button. Create an event, say addControlTextOnClick and assign the same event to each button.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void addControlTextOnClick(object sender, EventArgs e)
{
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";
}
}
You can even shorten this more using this C# construct:
private void addControlTextOnClick(object sender, EventArgs e) =>
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";

Visual Studio C #, Loop

Visual Studio C #
I made a calculator, and now I have to make a calculator memory (event).
There are 4 components other than the calculator: one Textbox for the answer of the calculator, two Buttons for "M" and "M+", and one Lable to display the answer again.
When the user clicks the “M” button, the contents of the Answer TextBox should be copied to a memory variable. Also make it so that when the user moves the mouse over the label, the value in the memory variable will appear in this label, and then disappear, when the mouse moves away from the label. Also add one more button, an “M+” button. When the user clicks this button, the contents of the Results box will be added to Memory. You will need to use a Global Variable to store this data.
My problem is that the label doesn't appear when the mouse over the label, and also it doens't disappear when the mouse leave the label. How can I fix it?
And also, is this way the right way to use the Global variable?
Below is my code (I just put the code for "M" and "M+" buttons, not the code for the calculator).
private String ans;
private Double answer;
private Double answerPlus;
private void btnM_Click(object sender, EventArgs e)
{
ans = txtDisplay.Text;
answer = double.Parse(ans);
lblblank.Text = answer.ToString();
}
private void lblblank_MouseEnter(object sender, EventArgs e)
{
lblblank.Show();
lblblank.Text = answer.ToString();
}
private void lblblank_MouseLeave(object sender, EventArgs e)
{
lblblank.Hide();
}
private void btnMplus_Click(object sender, EventArgs e)
{
answerPlus = answer + double.Parse(ans);
lblblank.Text = answerPlus.ToString();
}
Storing variables
The way you store your values is fine.
Events
Once you call .Hide() the next MouseEnter/MouseLeave-event will not be triggered anymore. What you could do is to take a panel, or any layout element as a wrapper/parent-element for the label and then adjust your event-callbacks to something like that:
private void panel_MouseEnter(object sender, EventArgs e)
{
lblblank.Show();
lblblank.Text = answer.ToString();
}
private void panel_MouseLeave(object sender, EventArgs e)
{
lblblank.Hide();
}
Edit
~~~
What does it mean that any layout element as a parent-element for the
label? Could you explain more?
What I meant was to just create a new panel (or layout-element) and put the label into it as a child. See the picture below:
If you set that up correctly, the code snippet I posted above will work just fine. This solution does not prevent the MouseLeave event from triggering when your mouse enters the label. Therefore you could use an alternative solution using the MouseMove event.
Alternative
using System;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
this.InitializeComponent();
// Subscribe to the MouseMove event
this.panel.MouseMove += this.panel_MouseMove;
}
private void panel_MouseMove(object sender, MouseEventArgs e)
{
// Checks if current mouse position is within the panel
if (this.panel.Bounds.Contains(new Point(e.X, e.Y)))
{
// Current mouse position within the panel
this.label.Show();
return;
}
// Current mouse position outside the panel
this.label.Hide();
}
}
}

How to let MainWindow know that in Folder/Page2.xaml a Button is clicked

In my MainWindow.xaml i have a Frame. The content of this Frame will change when i click a Button. So if I click a button to show private customers, the Frame Content shows the site of the private customers:
private void privatecustomer_Click(object sender, RoutedEventArgs e)
{
Main.Content = new Privatecustomer.privatecustomer_show();
}
After clicking this Button the Frame will change the Content.
The new Content shows Privatecustomer.privatecustomer_show.xaml now.
In this xaml i have another Button. If i click this Button, the Content of the Frame in MainWindow should change to "Privatecustomer.privatecustomer_add.xaml".
But How I can tell from privatecustomer_show.xaml to MainWindow.xaml, that the Button addPrivatecustomer in privatecustomer_show is clicked and that the Main.Content have to change to
Main.Content = new Privatecustomer.privatecustomer_add();
?
I hope I can get some help here
How about adding an event in privatecustomer_show.xaml that the MainWindow.xaml can subscribe to.
Like this
public event EventHandler AddPrivateCustomer;
protected virtual void OnAddPrivateCustomerEventArgs e)
{
if (AddPrivateCustomer!= null)
AddPrivateCustomer(this, e);
}
Update: Please note the updated code, I made a copy'n'paste mistake in my first version.
Change your:
private void privatecustomer_Click(object sender, RoutedEventArgs e)
To:
private void privatecustomer_Click(object sender, RoutedEventArgs e)
{
var privateCustomerContent=new Privatecustomer.privatecustomer_show();
privateCustomerContent.AddPrivateCustomer+=onClick_addPrivateCustomer;
Main.Content = privateCustomerContent;
}
private onClick_addPrivateCustomer(object o,EventArgs e)
{
// Change Main.Content
}
The idea is that your privatecustomer_show control sends events for when it want to change something outside of what it has access to.
You MainWindow which has full control of all its child windows will then subscribe to each event.
Set Page2.Tag property to the instance of your MainWindow (use Binding in Xaml, or simply set it in code). After the specified Button in Page2 is clickd, simply use the Tag property (which is now your MainWindow).
Another option is to use
App.Current.MainWindow
When the Button is clicked. (A warning. I don't know the structure your project and this might not be the instance you are looking for).

Windows Forms Remove Close Button

I'm working on a Windows Forms app and I'm wanting to remove the close button from the top. I'm aware of the ControlBox option, but I'm wanting to provide a help button. Is there a way to have the Close button not visible while maintaining the help button?
Your best bet may be to subcribe to the FormClosing event of the form like so and cancel the closing action:
// In your code somewhere subscribe to this event
Form1.FormClosing += Form1_FormClosing;
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
The benefit of doing this is that it prevents the user from closing the application from the close button and the taskbar.
Obviously you don't want to ALWAYS cancel the form from closing. So you will want to set some type of boolean flag that you will check in the event listener as to whether you want the form to be allowed to close or not. Example:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (BlockClosing)
e.Cancel = true;
}
EDIT: If you don't want to approach the problem that way, and you really do intend to completely remove the close button, then your best bet is to create your own custom title bar. In that case, you set the form's FormBorderStyle property to None. And you then dock your custom title bar to the top of the form. Here is some sample code from one I made a while back:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Spectrum.UI
{
public partial class TitleBar : UserControl
{
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler MinButtonClick;
public event EventHandler MaxButtonClick;
public event EventHandler CloseButtonClick;
#region Properties
[Category("Appearance")]
public string Title
{
get { return TitleLabel.Text; }
set { TitleLabel.Text = value; }
}
[Category("Appearance")]
public bool MinimizeEnabled
{
get
{
return minButton.Visible;
}
set
{
minButton.Visible = value;
}
}
[Category("Appearance")]
public bool MaximizeEnabled
{
get
{
return maxButton.Visible;
}
set
{
maxButton.Visible = value;
}
}
#endregion
public TitleBar()
{
InitializeComponent();
ShowTitleBarImage = false;
}
#region Mouse Events
private void TitleBar_MouseDown(object sender, MouseEventArgs e)
{
this.OnMouseDown(e);
}
private void TitleBar_MouseUp(object sender, MouseEventArgs e)
{
this.OnMouseUp(e);
}
private void TitleBar_MouseMove(object sender, MouseEventArgs e)
{
this.OnMouseMove(e);
}
#endregion
#region Button Click Events
private void minButton_Click(object sender, EventArgs e)
{
if (MinButtonClick != null)
this.MinButtonClick.Invoke(this, e);
}
private void maxButton_Click(object sender, EventArgs e)
{
if (MaxButtonClick != null)
this.MaxButtonClick.Invoke(this, e);
}
private void closeButton_Click(object sender, EventArgs e)
{
if (CloseButtonClick != null)
this.CloseButtonClick.Invoke(this, e);
}
#endregion
}
}
As you can see from the image, I also added a background image to the control. Depending on your patience and your requirements, you can use images and PictureBox controls to make this look as much like a standard title bar as you need.
In the above example I placed three buttons on the control with images I found online to represent minimize, maximize, and close. in your case you would simply exclude a close button. I also placed a string on the control with an appropriate font to serve as the title of the window.
Adding the custom title bar to your form is easy.
public TitleBar titleBar = new TitleBar();
titleBar.Dock = DockStyle.Top;
titleBar.MaximizeEnabled = true;
titleBar.MinimizeEnabled = true;
titleBar.Size = new System.Drawing.Size(10, 40); // Width doesn't matter - I wanted it 40 pixels tall
titleBar.Title = "Title Example";
titleBar.MinButtonClick += titleBar_MinButtonClick;
titleBar.Max ButtonClick += titleBar_MaxButtonClick;
this.Controls.Add(this.TitleBar);
And then last step is to set up your event listeners for the min and max button clicks:
private void titleBar_MinButtonClick(object sender, EventArgs e)
{
WindowState = FormWindowState.Minimized;
}
private void titleBar_MaxButtonClick(object sender, EventArgs e)
{
WindowState = FormWindowState.Maximized;
}
You may also note that I included events for mouse down, up and move in my title bar. This was so that I could create listeners in my form to move the form when the user clicked and dragged the title bar. This is optional and depends on if you need the user to be able to move your application window.
The added benefit of doing this is that can use the title bar for additional controls. For example, my application was custom written for use on a toughbook style tablet computer with a small touchscreen display. In my application, utilization of the limited space was extremely important. I was able to further modify what I've described here to also include menu bar style control directly on the title bar. In addition, I added more buttons to the left of the stand minimize, maximize, and close buttons. Really helped me utilize every square inch of the screen in my application. Couldn't have done it with the standard title bar.
Can you simply use Form.ControlBox = false (or via the designer as you point out rather negatively in your comment) and then add a custom help button on the form?
EDIT: A colleague of mine wrote an Excel add in and had a requirement to remove the X from certain forms (e.g. a Progress Bar that shouldn't be closed). He found a function written by Stephen Bullen that did just that. I've only seen this function used in VB, but perhaps you can get some ideas or direction out of his approach of using Windows API to solve your issue.
This code will disable the Close button. I am not sure if you can actually make it invisible. http://www.codeproject.com/Articles/20379/Disabling-Close-Button-on-Forms
//
// source code
// Code Snippet
private const int CP_NOCLOSE_BUTTON = 0x200;
protected override CreateParams CreateParams
{
get
{
CreateParams myCp = base.CreateParams;
myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON ;
return myCp;
}
}
Good luck!
Please try this.ControlBox = false.

Categories