I am using WPF and created a Window with informations about the computer.
It stores informations like Network connectivity, IP's, Subnet masks, Network devices and other stuff.
To track changes in the system I want to add an timer on an object to refresh itself. I don't want to refresh the hole form because I had HttpWebRequests in it and it will freeze the programm for a few seconds. It should be easier to see changes and to highlight them.
For example:
StComputerInf.Children.Add(new Label { Content = "2. Domain: \t\t" + System.Environment.UserDomainName });
I want to add here an timer to refresh itself.
And for every TreeViewItem in a TreeView:
public TreeView CreatTVConnection()
{
List<CAdapter> LAdapter = new List<CAdapter>();
List<TreeViewItem> lConnectedDevices = new List<TreeViewItem>();
List<TreeViewItem> lDisconnectedDevices = new List<TreeViewItem>();
LAdapter = ReadAdapter();
TreeView tv_Adapter = new TreeView();
tv_Adapter.Name = "Adapter";
tv_Adapter.Background = System.Windows.Media.Brushes.Transparent;
tv_Adapter.BorderThickness = new Thickness(0);
TreeViewItem Connected = new TreeViewItem();
TreeViewItem Disconnected = new TreeViewItem();
lConnectedDevices = LoadTV(true, LAdapter);
if (lConnectedDevices.Count > 0)
{
Connected.Header = "Connected:";
Connected.FontWeight = FontWeights.Bold;
Connected.Foreground = System.Windows.Media.Brushes.Green;
Connected.Name = "Connected";
foreach (TreeViewItem tvi in lConnectedDevices)
{
tvi.FontWeight = FontWeights.Normal;
Connected.Items.Add(tvi);
}
}
....
And is there a way to see if an object have changed? So I can highlight the affected object?
Use a factory to create your objects.
So for the label example you'd use something like
LabelFactory.Create(any useful parameters here) and as part of that method you can include a timer etc.
Also, look into using async/await to update your forms as an easier way to update them without freezing the forms. Once you are comfortable with the pattern you should be able to remove the dependency on timers.
Related
I'm working on an inventory program and have finished the main functionality as a command line console app. I am now working on a version for winforms. I want to enable it to dynamically generate a Groupbox that holds some textboxes. I'd rather not design 50+ lines of multiple textboxes. Keep in mind I'm rather new to programming, having started with C# a year ago. I know next to nothing on Winforms.
I've tried to use dynamic item = new Groupbox();as a similar method allowed generation of objects at runtime. In the command line app, the way it works is that based on information given, a certain amount of objects are passed into the list _AllItems. I was thinking of generating the Groupboxes by using:
private void InitializeGroupBox()
{
foreach (Product product in Product._AllItems)
{
dynamic Item = new GroupBox();
}
}
But I have the feeling I'm nowhere near the correct method. Thanks to anybody who helps.
You will need to learn a bit more, but here is what I usually do to achieve what you asked.
internal class DynamicForm : Form
{
private FlowLayoutPanel mFlowLayoutPanel;
public DynamicForm()
{
mFlowLayoutPanel = new FlowLayoutPanel();
mFlowLayoutPanel.Dock = DockStyle.Fill;
// Add to this Form
this.Controls.Add(mFlowLayoutPanel);
InitializeGroupBox();
}
private void InitializeGroupBox()
{
mFlowLayoutPanel.SuspendLayout(); // Performance
for (int i = 1; i <= 20; i++) {
var groupBox = new GroupBox();
groupBox.Text = "GroupBox #" + i;
groupBox.Size = new Size(200, 50);
var textBox = new TextBox();
textBox.Dock = DockStyle.Fill;
// Add the TextBox to GroupBox
groupBox.Controls.Add(textBox);
// Add to this Form
mFlowLayoutPanel.Controls.Add(groupBox);
}
mFlowLayoutPanel.ResumeLayout(); // after suspend, resume!
}
}
I'm used to working with HTML apps in which there is no problem of creating a list of components (like phonebook contact).
Example of my app
Now I have used this method in Windows Forms. One panel is populated with large number of custom UserControls (UC). Only 30 UserControls takes more than 5s to render. While the list of data from database is returnd in <1s.
This UC has only labels, PictureBox and Click event. It's called like this. This data is used to populate child controls.
new UserControl(MyModel data);
Is there a beter way of doing this? I would like to have user friendly GUI and not using Grid layout. It's not user friendly and very limited in terms of how data can be showed to user.
foreach (var data in myDbResult)
{
var uc = new MyUserControl(data);
uc.Dock = DockStyle.Top;
resultFlowPanel.Controls.Add(uc);
}
...
public MyUserControl(MyModel data)
{
this.data = data;
InitializeComponent();
label1.Text = data.name;
label2.Text = data.address;
// get some more data from database
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["AlarmDbCon"].ConnectionString))
{
payer = db.Query<Models.g_UserPayer>("SELECT TOP(1) * FROM g_UserPayer WHERE ID_User=#UserID", new
{
UserID = guard.ID_User
}).FirstOrDefault();
}
label3.Text = payer.email;
PictureBox.Image = payer.image;
}
If there are your own UI controls then you should consider using in your Load method the this.SuspendLayout(); and this.ResumeLayout(); methods to postpone the expensive UI layout calculation of the UI control.
Another aspect you can use is to load your expensive data on a Background Thread. To be able to do this you should use delegates to be able to update controls created on the UI Thread. More info on this on this SO answer.
I am using TestStack White to automate a test scenario in a Telerik Winforms application.
The application has plenty of elements and if I directly search for an element, the run would just stale and never end.
So I did the manual hierarchy search to dig into element levels to save the performance and make it work.
Then the code looks in a not neat way as I heavily use foreach to do the loop myself.
Is this the proper way to cope with the performance or we have better ways to
have both neat code and good performance when using TestStack White?
Cheers:
[TestMethod]
public void TestMethod1()
{
CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
var applicationDirectory = "D:\\Software\\ABC Handling System";
var applicationPath = Path.Combine(applicationDirectory, "ABCClient.GUI.exe");
Application application = Application.Launch(applicationPath);
Thread.Sleep(5000);
Window window = application.GetWindow("[AB2] - ABC Handling System 2 - [Entity Search]", InitializeOption.NoCache);
ListBox listbox = window.Get<ListBox>();
ListItem dispatchButton = listbox.Items.Find(i=>i.Name.Equals("Display"));
dispatchButton.Click();
Thread.Sleep(5000);
SearchCriteria sc = SearchCriteria.ByControlType(ControlType.Pane).AndIndex(3);
UIItemContainer groupbox = (UIItemContainer)window.MdiChild(sc);
UIItemContainer pane1 = null;
foreach (IUIItem automationElement in groupbox.Items)
{
if (automationElement.Name == "radSplitContainer1")
{
pane1 = (UIItemContainer)automationElement;
break;
}
}
UIItemContainer pane2 = null;
foreach (IUIItem automationElement in pane1.Items)
{
if (automationElement.Name == "splitPanel1")
{
pane2 = (UIItemContainer)automationElement;
break;
}
}
Table table = null;
foreach (IUIItem automationElement in pane2.Items)
{
if (automationElement.Name == "Telerik.WinControls.UI.RadGridView ; 247;14")
{
table = (Table)automationElement;
break;
}
}
TableRow row = table.Rows[9];
string s = row.ToString();
}
Update on 05/11/2019:
It turned out TestStack White is not the best solution for my multi-row grid app regarding its performance.
Actually we managed to have developed something from the grid side by developers, and we hook those functions up. So we were using AutoIt + Customized Application side hooks. Some really good controls there.
Yes, we succeeded in this approach.
Yeah instead of doing the looping yourself you would just use the Get on each of the retrieved elements. Oddly when I dig down through the code it is using AutomationElement.FindFromPoint which I am not sure how it traverses the tree to find it's elements. I would give the follow code a try and see if it is more or less performant in your application.
CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
var applicationDirectory = "D:\\Software\\ABC Handling System";
var applicationPath = Path.Combine(applicationDirectory, "ABCClient.GUI.exe");
Application application = Application.Launch(applicationPath);
Thread.Sleep(5000);
Window window = application.GetWindow("[AB2] - ABC Handling System 2 - [Entity Search]", InitializeOption.NoCache);
ListBox listbox = window.Get<ListBox>();
ListItem dispatchButton = listbox.Get<ListItem>(SearchCriteria.ByText("Display"));
dispatchButton.Click();
Thread.Sleep(5000);
UIItemContainer groupbox = window.MdiChild(SearchCriteria.ByControlType(ControlType.Pane).AndIndex(3));
UIItemContainer pane1 = groupbox.Get<UIItemContainer>(SearchCriteria.ByText("radSplitContainer1"));
UIItemContainer pane2 = pane1.Get<UIItemContainer>(SearchCriteria.ByText("splitPanel1"));
Table table = pane2.Get<Table>(SearchCriteria.ByText("Telerik.WinControls.UI.RadGridView ; 247;14")); ;
TableRow row = table.Rows[9];
string s = row.ToString();
I am getting everything from the previous element in an attempt to limit the portion of the tree it is scanning. From the looks of White's code this may have no affect other than reducing the amount of boiler plate on the front end depending on the performance of AutomationElement.FindFromPoint.
Also the ByText search criteria maps to the name property on a AutomationElement by calling CreateForName which is why I replaced all the checks against name with ByText.
This may be a noobish question, but in my records in Coded UI Tests, I have recorded a lot of controls that don't have enough defined properties to be found in playback.
For exemple:
public HtmlEdit UIItemEdit
{
get
{
if ((this.mUIItemEdit == null))
{
this.mUIItemEdit = new HtmlEdit(this);
#region Search Criteria
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Id] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Name] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.LabeledBy] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Title] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Class] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.ControlDefinition] = "type=\"text\" value=\"\"";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.TagInstance] = "5";
this.mUIItemEdit.WindowTitles.Add("http://cms.home.psafe.com/");
#endregion
}
return this.mUIItemEdit;
}
In this post, I learned about SearchProperties, but it doesn't look to be an appropriate solution in this case.
Is there any other way to wrap these controls properly?
You might be able to find it if its containing element can be found. You can use the containing element to scope the search. So, find that element's parent, then find an input type=text within it:
var container = new HtmlControl(bw); //where bw is the browser window
HtmlDiv parentDiv = new HtmlDiv(container);
parentDiv.SearchProperties[HtmlDiv.PropertyNames.Id] = "theIdOfYourDiv";
HtmlEdit edt = new HtmlEdit(parentDiv); //the search scope is narrowed down to the div only. This may be enough to find your control with the search property.
edt.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
You have two options:
Try crowcoder's solution of searching in the parent. The problem with this solution is when you move a control around you're going to be changing code a lot.
Add an Id property to all your controls in the HTML, this will make your Coded UI more robust and responsive to changes in the UI.
I'm very new to WPF and I'm attempting to create a treelist navigation within each navbar group. Because the number of navbar groups and treelists are dynamic I have to make them in code rather than them be pre-defined in XAML.
I have tested the following so far which is meant to define the navbar group's content rather than use the default item
private void CreateGroup2(NavBarControl navBar)
{
NavBarGroup group2 = new NavBarGroup();
group2.Header = "Custom Content";
//Specify that the group's content should be defined via the Content property
group2.DisplaySource = DisplaySource.Content;
TreeListControl tree = new TreeListControl();
tree.ItemsSource = TreeList_DataBinding.Stuff.GetStuff();
group2.Content = tree;
navBar.Groups.Add(group2);
}
This gives an Exception: Grid.InfiniteGridSizeException: By default, an infinite grid height is not allowed since all grid rows will be rendered and hence the grid will work very slowly. To fix this issue, you should place the grid into a container that will give a finite height to the grid, or you should manually specify the grid's Height or MaxHeight. Note that you can also avoid this exception by setting the TreeListControl.AllowInfiniteGridSize static property to True, but in that case the grid will run slowly.
I'm a little confused as I'm not using a grid? Can anyone give any pointers what's wrong and how I can add a treview under each navbar group?
Thank You
It feels a bit wrong answering my own question but I managed to get it working using the following
private void CreateGroup2(NavBarGroup navBarGroup)
{
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
TreeViewItem nod = new TreeViewItem();
nod.Header = "Tree Node1";
treeview.Items.Add(nod);
TreeViewItem nod1 = new TreeViewItem();
nod1.Header = "Tree Node2";
treeview.Items.Add(nod1);
TreeViewItem nod2 = new TreeViewItem();
nod2.Header = "Tree Node3";
nod1.Items.Add(nod2);
//StackPanel stcPnl = new StackPanel(); /optiona
//stcPnl.Children.Add(treeview);
//navBarGroup.Content = stcPnl;
navBarGroup.Content = treeview;
navBarGroup.DisplaySource = DisplaySource.Content;
}