Where do I have to put the code? - winforms

I have an application which downloads some data and I want to show that data on a listView. By deafault Mfc shows me some code, a namepace and a class with that listView. There is also a seperate cpp file with a main with this code:
int main(array<System::String ^> ^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Hauptfenster erstellen und ausführen
Application::Run(gcnew Form1());
return 0;
}
I don't know where to put my function in this main which downloads stuff, and how to address and alter this listView
inside the main this doesn't work:
Form1->listView1->Text = "asdasdasdasd"

This is not MFC, this is C++/CLI with Windows Forms. You need to place your code to some Form1 event handler, for example, Load event handler. Double-click Form1 in Design view to create event handler, and place your code there.
Later you may improve the program logic by handling some button events (for example, add Download button and handle its Click event) and using background threads. But on the first step just try Form.Load event.

A quick solution:
Form1 theForm = gcnew Form1();
theForm->listView1->Text = "Text here";
Application::Run(theForm);
But you should implement the same in one of the events for Form (like Load event).

Related

Winforms OpenFileDialog problems

I would like to create a file dialog form that works like the one in MS Word. Specifically, you can select an existing file, create a new one, or delete a file, all within the file dialogue window. OpenFileDialog is a read-only deal. You can't create a new file with that control. SaveFileDialog can't be used to select an existing file. Is there a control that will do all of the above?
To create your custom dialog you should follow these steps:
Create a new form, call it MyFolderDialog
In the new form add the controls you need. For example, in order to let the user browse the file system, you can add a treeView and populate it using the classes System.IO.FileInfo and System.IO.FolderInfo.
Create a property in your form that reflects the user choice, something like public string SelectedFolder
Add a "Confirm" button and a "Cancel" button to your form
In the cancel button click event handler add the code this.DialogResult = DialogResult.Cancel;
In the confirm button click event handler add the code this.DialogResult = DialogResult.Ok;
Call your custom dialog like that
private string PickAFolder()
{
string selectedFolder = string.Empty;
using(var f = new MyFolderDialog())
{
if(f.ShowDialog() == DialogResult.Ok)
{
selectedFolder = f.SelectedFolder;
}
}
return selectedFolder;
}
Note that when the form is shown using ShowDialog method, the form won't dispose when it will be closed, so you can still read his properties after you confirmed. So it a good practice dispose it manually when you don't need it anymore. An elegant way to do it is to use the using keyword.
I hope this helps.

ShowDialog() behind the parent window

I am using ShowDialog() with WindowStyle = WindowStyle.SingleBorderWindow; to open a modal window in my WPF (MVVM) application, but it lets me navigate to parent window using the Windows taskbar (Windows 7).
I've found an answer here: WPF and ShowDialog() but it isn't suitable for me because I don't need an "always on top" tool window.
Thanks in advance
Try setting the Owner property of the dialog. That should work.
Window dialog = new Window();
dialog.Owner = mainWindow;
dialog.ShowDialog();
Edit:
I had a similar problem using this with MVVM. You can solve this by using delegates.
public class MainWindowViewModel
{
public delegate void ShowDialogDelegate(string message);
public ShowDialogDelegate ShowDialogCallback;
public void Action()
{
// here you want to show the dialog
ShowDialogDelegate callback = ShowDialogCallback;
if(callback != null)
{
callback("Message");
}
}
}
public class MainWindow
{
public MainWindow()
{
// initialize the ViewModel
MainWindowViewModel viewModel = new MainWindowViewModel();
viewModel.ShowDialogCallback += ShowDialog;
DataContext = viewModel;
}
private void ShowDialog(string message)
{
// show the dialog
}
}
I had this problem but as the Window was being opened from a view model I didn't have a reference to the current window. To get round it I used this code:
var myWindow = new MyWindowType();
myWindow.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
You can use: myWindow.Owner = Application.Current.MainWindow;
However, this method causes problems if you have three windows open like this:
MainWindow
|
-----> ChildWindow1
|
-----> ChildWindow2
Then setting ChildWindow2.Owner = Application.Current.MainWindow will set the owner of the window to be its grandparent window, not parent window.
When the parent window makes (and shows) the child window, that is where you need to set the owner.
public partial class MainWindow : Window
{
private void openChild()
{
ChildWindow child = new ChildWindow ();
child.Owner = this; // "this" is the parent
child.ShowDialog();
}
}
Aditionally, if you don't want an extra taskbar for all the children... then
<Window x:Class="ChildWindow"
ShowInTaskbar="False" >
</Window>
Much of the reason for the MVVM pattern is so that your interaction logic can be unit tested. For this reason, you should never directly open a window from the ViewModel, or you'll have dialogs popping up in the middle of your unit tests.
Instead, you should raise an event that the View will handle and open a dialog for you. For example, see this article on Interaction Requests: https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec12
The problem seems to be related to Window.Owner, and indeed if you judge by previous knowledge that you might have of the Win32 API and WinForms, a missing owner would be the typical cause of such a problem, but as many have pointed out, in the case of WPF that's not it. Microsoft keeps changing things to keep things interesting.
In WPF you can have a dialog with a specific owner and you can still have the dialog appear in the taskbar. Because why not. And that's the default behavior. Because why not. Their rationale is that modal dialogs are not kosher anymore, so you should not be using them; you should be using modeless dialogs, which make sense to show as separate taskbar icons, and in any case the user can then decide whether they want to see different app windows as separate icons, or whether they want to see them grouped.
So, they are trying to enforce this policy with complete disregard to anyone who might want to go against their guidelines and create a modal dialog. So, they force you to explicitly state that you do not want a taskbar icon to appear for your dialog.
To fix this problem, do the following in the constructor of your view class:
ShowInTaskbar = false;
(This may happen right after InitializeComponent();
This is equivalent to Xcalibur37's answer, though the way I figure things, since WPF forces you to have both a .cs file and a .xaml file, you might as well put things that are unlikely to change in the .cs file.
Add "ShowInTaskbar" and set it to false.
Even if this post is a bit old, I hope it is OK that I post my solution.
All the above results are known to me and did not exactly yield the desired result.
I am doing it for the other googlers :)
Lets say f2 is your window that you want to display on top of f1 :
f2.Owner = Window.GetWindow(this);
f2.ShowDialog();
That's it , I promise it will not disappear !
HTH
Guy

wpf BackgroundWorker - Regarding updating UI

I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.

My application loses focus when a window is closed

I have a simple two forms, one that contains a grid and a button. When I click the button, my application starts doing a long operation. While it is working, I show another form that contains a progress bar
I open it like this:
_busyWindow.ShowDialog();
And defined
public partial class BusyWindow : DevExpress.XtraEditors.XtraForm
{
public BusyWindow()
{
InitializeComponent();
}
private void BusyWindow_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true; // this cancels the close event.
}
}
When the operation is finished, I hide the form like this
if (ended)
_busyWindow.Hide();
It works fine. The problem is that when I close the second form (same closing code), it also closes fine but my main GUI loses the focus. For example, if I have the Firefox opened behind the application, then the Firefox gets the focus.
This only happens when I close the second form when the busyWindow has been opened, and no when it hasn't (ie, if I open the form, I close it without clicking on the button, then the main GUI doesn't lose the focus).
Do you know what is happening or where could I try to search?
There could be two possible solutions to enable you to keep focus on your main window:
//Edited: Main Window in the below example would be the window with Grid and Button.
Since you are showing the busy window via ShowDialog() try setting the owner of the window by this: _busyWindow.ShowDialog(this);. I had earlier faced a similar problem and this worked for me. Since you specify the owner of the busyWindow, when it closes it would put the focus back on its owner,i.e. your main window
In case the above technique doesnt work (it should, as it worked for me), you could try to pass the reference of the main window to the busyWindow and then on its close set the focus of the main window. Sample:
_busyWindow.MyMainWindow = this; //MyMainWindow references mainWindow of your app
_busyWindow.ShowDialog();
And the following at the FormClosing of busyWindow:
private void BusyWindow_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = true; // this cancels the close event.
MainWindow.Focus();
}
See if it works. The first solution should work.
Hope it helps.
Thanks & Happy Windowing!
Just set child's window Owner = null before closing it

Add a Load event for Winforms Control just as Form class

is there a way I can get a Load event for System.Windows.Forms.Control just like System.Windows.Forms.Form.Load?
I want to run some initialize code before the control first shown.
Also, it would be nice to be able to do the same for System.Windows.Forms.ToolStripStatusLabel which is not actually a Control, but works like one.
Ideally, I can do this:
control.OnLoad(() => { dosomething here; });
in which OnLoad is a extension method that would run the argument Action when the "control" "Loads".
Thanks!
Form.Load event is called by the OnLoad method which is called from the OnCreateControl method which belongs to the Control class. So for the form the calling sequence would be following:
OnCreateControl start
OnLoad start
Form Load event call
OnLoad finish
OnCreateControl finish
I guess you can override OnCreateControl for your component and add your optimization code there.
Hope this helps, Regards.
For a control you can override either OnControlCreated or OnHandleCreated. The latter one can fire multiple times if it is necessary to recreate the control window. Be sure to use it if your code affects the window itself. In other words, if you do anything that requires the Handle property.
Few suitable choices for a ToolStripItem derived control. I'd recommend overriding SetVisibleCore() or OnAvailableChanged() or the AvailableChanged event. They run when the Visible property of the ToolStripItem changes. Beware that it may fire multiple times, keep a bool field that tracks that your initialization code has already run.
Last but not least, be sure to only do any of this if your code actually requires the control to be created. The vast majority of init code can go in the constructor. You only need a Load event if your code depends on the actual Location and Size of the control. Which might be different from the designer value if the form rescales itself due to a different system font or video DPI setting on the target machine.
I needed a solution like this for a TabPage within a TabControl.The only thing I came up with was using the paint event handler. I added the event handler for Paint and in the very first line I remove the event handler and then do more initialization code. This only works if you do nothave any custom painting. Alternatively, if you do need to do custom painting you could add a flag to check for each time Paint Executes.
//Paint only runs once
private void tabPage1_Paint(object sender, PaintEventArgs e)
{
tabPage1.Paint -= tabPage1_Paint;
//Do initialization here
}
/////////////////////////////////////////////////////////////////////////////////
//Paint always runs
private bool IsFirstPaint = true;
private void tabPage1_Paint(object sender, PaintEventArgs e)
{
if(IsFirstPaint)
{
IsFirstPaint = false;
//Do initialization here
}
//Do custom painting here
}

Resources