WPF multiwindow application. Determining dimensions of child windows - c#

I have a multi-window application in WPF, where my main application window is invisible (Visibility=Collapsed) containing visible child windows. Application creates child windows on-demand. I need an algorithm to determine the coordinates and dimensions of the newly created child window. Obviously, the new child window should not cover (fully) another child window. Does WPF offer any support whatsoever to implement this kind of logic ? Or, do I have to do everything on my own. I imagine this would be a lot of work. The behavior I am looking for is very similar to Sticky Notes behavior in Windows 7.
Part of my code will help you to really understand what I mean:
public void ViewModelsCollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (ViewModel viewModel in e.NewItems)
{
View view = new View(viewModel);
view.Owner = SleekNoteUI.App.Current.MainWindow;
...
}
}
}

Related

Delay / Freezing OF WPF ui

I have been working on a WPF application, and my application freezes as I Navigate from one window to another, The first window is for Login and the second window is the main window where work is carried out (the application is a form based application where Data is Written to and from a Database like MySQL)
2nd Window Operations
In the second window, I have created a Menu by placing Icons in a List and then Handling their click events to navigate to a page
These Pages are not the UI Element 'Page' but are Usercontrols I refer to them by Pages for Simplicity, and these usercontrols are added to the grid by grid.children.Add(UsercontrolName);. . . . . . This part of adding usercontrols as you can see is done in code behind(C#) and not by XAML
Click Event Handling
All i do is I simply shift the visibilty of a Usercontrol, so for example an icon that is related to a say Usercontrol A, that usercontrols visibity is set to visible and all other usercontrols visibilty is set to Collapsed, by default all usercontrols Visibilty is set to Collapsed
The Problem
What I do not Understand is what is causing this Lag or Freeze or even how to find or figure this out, I understand that WPF is an STA and UI elements such as a Grid/usercontrols should be run on the main thread , so if we Used Dispatcher to resume to the Main thread the UI wont freeze but it would Still take time to load, and I want to stop this delay
Although Contradictory I still think its the Adding of Usercontrols to the Grid is causing slow speeds, bcz these Usercontrols have numerous UI elements that I am using from a library called MaterialDesigninXAML, its a form based application so have like 8 Usercontrols all of them with textboxes and Imageboxes and buttons etc, and adding these Usercontrols at once is causing the Overhead
And I am stuck at this Delay / Freeze of the UI
The code below is a ditto example of my original code, I posted the example bcz my code is lengthier with the same problem
this is the part of the code that lags
Dispatcher.InvokeAsync(() =>
{
UserControl usc;
usc = new Y1();
usc.Tag = "Memeber";
no.Children.Add(usc);
Y4 usc2 = new Y4();
usc2.Tag = "CheckBoxList";
no.Children.Add(usc2);
Y5 usc3 = new Y5();
usc3.Tag = "Reports";
no.Children.Add(usc3);
ShowUserContro("Memeber");
}
);
private void ShowUserContro(string v)
{
foreach (UIElement item in no.Children)
{
if (item is UserControl)
{
UserControl x = (UserControl)item;
if (x.Tag != null)
{
if (x.Tag.ToString().ToUpper() == v.ToUpper())
{
x.Visibility = Visibility.Visible;
}
else
{
x.Visibility = Visibility.Collapsed;
}
}
}
}
}
private void ListViewMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ShowUserContro((((ListViewItem)((ListView)sender).SelectedItem).Name));
}

Updating MDI child forms list after closing a form

I am using DevExpress NavBar as main menu for my MDI application, and one of NavBar's groups contains items that represent opened MDI child forms. I am having trouble with updating a menu when a MDI child form closes.
I have to use Form.MdiChildren collection to generate menu group, but the problem is, when using Form.FormClosing event, that closed form is still in Form.MdiChildren collection. I tried to use a System.Timers.Timer to wait 1 second and then update a menu, but I get various exceptions because of asynchronous behavior (when a user closes few forms very fast).
I also cannot maintain my own list of MDI children, because of complexity of classes design.
Does anyone have some elegant solution for this?
I have had success with using this combination of methods:
private List<Form> _childForms = new List<Form>();
protected override void OnMdiChildActivate(EventArgs e)
{
base.OnMdiChildActivate(e);
Form form = ActiveMdiChild;
if (form == null)
return;
else
{
if (!_childForms.Contains(form))
{
_childForms.Add(form);
form.FormClosed += mdiChildForm_FormClosed;
}
}
}
private void mdiChildForm_FormClosed(Object sender, FormClosedEventArgs e)
{
var form = (Form)sender;
if (_childForms.Contains(form))
_childForms.Remove(form);
if (_childForms.Count > 0)
_childForms[_childForms.Count - 1].Activate();
}
Note that the Activate method is called pretty much anytime the user interacts with a child form. That includes opening and closing them.
You can then make use of the childForms collection to always know the open forms and do what you like with them.
"I also cannot maintain my own list of MDI children, because of complexity of classes design."
Is this because of the different class types?
What about holding a list of base classes? like: List<Form> When there is a FormClosed event, just remove that form from the list.

Add overlay to Window at runtime

I'm writing a simple "tutorial" library that will allow developers to easily add step-by-step tutorials to their existing WPF applications. The tutorials will help first time users of the application find their way around by adding an overlay that highlights a control and explains its purpose. The end result will look something like this:
The regular application:
The overlay explaining the purpose of a control:
My question is this: What's the most reliable and unobtrusive way to inject the overlay view into the current window? The best I've come up with so far is to require the developer to add an attached property to whatever window will be hosting the overlay, and then add the necessary elements on the window's Initialized callback:
public static void IsTutorialOverlayCompatibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((Boolean)e.NewValue == true)
{
if (sender as Window != null)
{
Window window = (Window)sender;
window.Loaded += new RoutedEventHandler((o, eargs) =>
{
Grid newRootElement = new Grid();
newRootElement.Name = "HelpOverlayRoot";
if (window.Content as UIElement != null)
{
UIElement currentContent = (UIElement)window.Content;
window.Content = null;
newRootElement.Children.Add(currentContent);
newRootElement.Children.Add(new HelpOverlayControl());
window.Content = newRootElement;
}
});
}
}
}
This feels like a hack, however, and I'm not sure that there isn't some edge case where this method will break the layout of the application. In addition, it requires that the window's Content property be an instance of type UIElement.
I'd like to avoid forcing developers to change their XAML (i.e, adding a custom overlay UserControl to every window) in order to use my library. What's the best way to add this kind of functionality to an existing WPF application?

customizing treeview in winforms

Is it possible to create a treeview in visual studio which resembles the following figure :
The ROOT , CHILD and Sub-Child , all three would be LinkLabels , and on clicking them a new Form would be opened.
You could also try embed WPF user control into WinForm. Customizing WinForms isn't an easy task. In WPF you can do it much easier.
You also can activate Hot tracking for the tree view and then handle the NodeMouseClick event.
This is not practical in Winforms, every Control has a native Windows window associated with it. A window is a very expensive operating system object, create more than 50 of them and your user interface will noticeably start to drag because of the amount of overhead involved in drawing the controls. You very quickly reach that practical upper limit by nesting controls like you are intending to do.
You can customize the appearance of a TreeView by using its DrawMode property and the DrawNode event. The MSDN library article for TreeView.DrawNode has a decent example. It is also a popular component type in 3rd party component vendor collections. They add lots of bells and whistles to their version.
So, people don't like doing it.
The answer, however, is Yes, you can.
TreeView treeView1;
void Initialize_It() {
treeView1 = new TreeView();
treeView1.AfterSelect += new TreeViewEventHandler(treeView1_AfterSelect);
TreeNode Root = treeView1.Nodes.Add("ROOT");
TreeNode Child = Root.Nodes.Add("CHILD");
TreeNode SubChild = Child.Nodes.Add("Sub-Child");
}
void treeView1_AfterSelect(object sender, TreeViewEventArgs e) {
const string FORMAT = "{0} Node Selected. Call your Windows Form from here.";
if (e.Node.Level == 0) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
} else if (e.Node.Level == 1) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
} else if (e.Node.Level == 2) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
}
}

How to implement the Edit -> Copy menu in c#/.net

How do I implement a Copy menu item in a Windows application written in C#/.NET 2.0?
I want to let the user to mark some text in a control and then select the Copy menu item from an Edit menu in the menubar of the application and then do a Paste in for example Excel.
What makes my head spin is how to first determine which child form is active and then how to find the control that contains the marked text that should be copied to the clipboard.
Help, please.
With the aid of some heavy pair programming a colleague of mine and I came up with this, feel free to refactor.
The code is placed in the main form. The copyToolStripMenuItem_Click method handles the Click event on the Copy menu item in the Edit menu.
/// <summary>
/// Recursively traverse a tree of controls to find the control that has focus, if any
/// </summary>
/// <param name="c">The control to search, might be a control container</param>
/// <returns>The control that either has focus or contains the control that has focus</returns>
private Control FindFocus(Control c)
{
foreach (Control k in c.Controls)
{
if (k.Focused)
{
return k;
}
else if (k.ContainsFocus)
{
return FindFocus(k);
}
}
return null;
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
Form f = this.ActiveMdiChild;
// Find the control that has focus
Control focusedControl = FindFocus(f.ActiveControl);
// See if focusedControl is of a type that can select text/data
if (focusedControl is TextBox)
{
TextBox tb = focusedControl as TextBox;
Clipboard.SetDataObject(tb.SelectedText);
}
else if (focusedControl is DataGridView)
{
DataGridView dgv = focusedControl as DataGridView;
Clipboard.SetDataObject(dgv.GetClipboardContent());
}
else if (...more?...)
{
}
}
Why not extending the control, so the control itself provides the data which should be copied into the clipboard.
Take a look at ApplicationCommands documentation.
To determine which window is open, you can query the Form.ActiveMDIChild property to get a reference to the currently active window. From there, you can do one of two things:
1) If you create your own custom Form class (FormFoo for example) that has a new public member function GetCopiedData(), then inherit all of your application's child forms from that class, you can just do something like this:
((FormFoo)this.ActiveMDIChild).GetCopiedData();
Assuming the GetCopiedData function will have the form-specific implementation to detect what text should be copied to the clipboard.
or
2) You can use inheritance to detect the type of form that is active, and then do something to get the copied data depending on the type of form:
Form f = this.ActiveMDIChild;
if(f is FormGrid)
{
((FormGrid)f).GetGridCopiedData();
} else if(f is FormText) {
((FormText)f).GetTextCopiedData();
}
etc.
That should get you started with finding the active window and how to implement a copy function. If you need more help copying out of a GridView, it may be best to post another question.
If the form is tabbed and the target control is a DataGridView, it's sometimes possible for the Form's TabControl to be returned as the active control, using the above method, when the DataGridView is right clicked upon.
I got around this by implementing the following handler for my DataGridView:-
private void dataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
dataGridView.Focus();
dataGridView.CurrentCell = dataGridView[e.ColumnIndex, e.RowIndex];
}
}
It seems to me that you might be better off breaking this into smaller tasks/questions.
You have a few issues you are stuck on from the way it sounds.
You have multiple 'child' windows open. Is this an MDI application?
When an action is performed on one of those child windows, it should fire an event in that window's event handlers. That is your first thing to set up. If this is a datagridview I would suggest a simple test to start. Try trapping the DataGridView.SelectionChanged event. Just throw in something like MessageBox.Show("I copied your datas!"); for now.
This should get you started where you will at least understand how this event will be raised to you.
From here, we will need to know a little more about your datagrid, and the rows and child controls in those rows. Then we can likely create events in the render events that will be raised at the appropriate times, with the appropriate scope.

Categories

Resources