I have a program that runs in the system tray that communicates with our server and "syncs" data based on a users preferenced jobs. The idea is similar to Dropbox, but for our surveying software called 12d Synergy. The idea is that users can sync data without needing to navigate through the softwares Client.
I want to add the functionality so that when the program is syncing, the icon in the system tray changes to indicate that its still syncing, but i can't figure out how to get access to the original object within the portion of the program where the event is located.
My program stucture is as follows (in c#):
Program.cs
using (ProcessingIcon pi = new ProcessingIcon())
{
pi.SetIcon(Resources._12d);
pi.Display();
Application.Run();
}
ProcessingIcon.cs
NotifyIcon ni;
public void SetIcon(Icon path)
{
ni.Icon = path;
}
public void Display()
{
ni.Text = "Sunrise Surveying 12d Synergy Sync Tool";
ni.Visible = true;
ni.ContextMenuStrip = new ContextMenus().Create();
}
ContextMenus.cs
public ContextMenuStrip Create()
{
// Sync Now
item = new ToolStripMenuItem();
item.Text = "Sync Now";
item.Click += new EventHandler(syncNow_Click);
item.Image = Resources.Sync.ToBitmap();
cms.Items.Add(item);
}
void syncNow_Click(object sender, EventArgs e)
{
string[] jobs = Sync.GetSharedFiles();
string[] files = Sync.GetDataToSync(jobs);
Sync.SyncData(files);
}
What i want to happen, is in the syncNow_click, call the ProcessingIcon.SetIcon() to change the icon, but i can't figure out how to get access to an object that exists 3 layers up in the program.
I should note that i am not a programmer, i'm a surveyor with an interest in programming. I am completely self taught, so i know there is probably something relatively simple i'm missing. This is also my first post in StackOverflow, so i'm not 100% how to use this site to the full capability, so if this has been answered somewhere i apologise.
Any help or advice would be greatly appreciated.
So i worked out a way to answer my own question. Just putting it here in case anyone has the same issue. It turned out to be incredibly simple, and purely just by me not fully understanding the classes/objects structure.
I added a constructor for my ContextMenus object which passed in the the NotifyIcon that was calling it. This was passed to a NotifyIcon variable in that class which i could then access.
class ContextMenus
{
public NotifyIcon ni;
public ContextMenus(NotifyIcon ni)
{
this.ni = ni;
}
}
Related
Stuck with a problem with WPF. I should mention that I'm very very new to WPF. I'm building small apps for myself to understand the topics.
At the moment I'm stuck updating a listbox calling a class that is in my "_classes" folder which gets information from a remote computer. The reason I put it in a different folder was to avoid all the mess behind the XAML. I can get the GUI freezing issue fixed if I want to put my code behind the XAML which is not ideal from what I've been reading.
The examples given or searched on here or other sites are kind of confusing with no explanations. It would be awesome if someone can actually put comments where I'm stuck and point out what I was doing wrong after they correct it. After all I'm trying to learn this. Going forward, whats the best way to implement these kind of long processing tasks? Create a folder? Call classes? Different solutions? Different projects? etc. I've been reading a lot about this and everyone seems to have their own opinion on this.
Also, I searched this and gotten no where. I feel like I'm going to be the first one to ask this but is MVVM necessary for responsive UI? Can I just implement async/await and be done with it like I'm trying to do in the example I have below?
This is the code I have at the moment. Although I get the results I want, the GUI is unresponsive. I added the thread.sleep there to simulate a long process.
Although I tried different things, this is the latest code I have at the moment.
This is what I had in mind that the app would do:
Click on a button. Listbox displays "getting information"
The process is running in the background (gathering information)
GUI is responsive and I can do other things on the GUI (minimize, change tabs,etc).
Once the process is done, add the info to the Listbox.
Thank you everyone in advance.
PS. Please ignore the naming conventions for now. I've been working on this for a while and just gave up on that part till I actually fix the issue.
XAML
<StackPanel>
<Button Name="test" Height="30" Width="70" Background="red" Content="Submit"
Click="test_Click" />
<ListBox x:Name="listboxResult" />
</StackPanel>
Code Behind XAML
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void test_Click(object sender, RoutedEventArgs e)
{
listboxResult.Items.Clear();
listboxResult.Items.Add("Getting listbox results...");
try
{
await Task.Factory.StartNew(() =>
{
getResults("Passing String Argument", listboxResult);
});
}
catch (Exception)
{
throw;
}
}
private void getResults(string v, ListBox listBoxIn)
{
this.Dispatcher.Invoke((Action)(() =>
{
ReturnListbox _result = new ReturnListbox(v, listBoxIn);
}));
}
}
My class in _classes folder
public class ReturnListbox
{
private ListBox _myListBox;
private string _ComputerName;
public ListBox MyListBox
{
get { return _myListBox; }
set { _myListBox = MyListBox; }
}
public string CName
{
get { return _ComputerNAme; }
set { _ComputerName = CName; }
}
public ReturnListbox(string ComputerName, ListBox IncomingListBox)
{
BuildListBox(ComputerName, IncomingListBox);
}
private void BuildListBox(string CName, ListBox MyListBox)
{
Thread.Sleep(5000);
_myListBox = MyListBox;
MyListBox.Items.Clear();
try
{
ManagementScope Manage = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", CName));
Manage.Connect();
ObjectGetOptions objectOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_OperatingSystem");
ManagementClass Class = new ManagementClass(Manage, managementPath, objectOptions);
foreach (ManagementObject Object in Class.GetInstances())
{
// Display the remote computer information
MyListBox.Items.Add(string.Format("Computer Name : {0}", Object["csname"]));
MyListBox.Items.Add(string.Format("Windows Directory : {0}", Object["WindowsDirectory"]));
MyListBox.Items.Add(string.Format("Operating System: {0}", Object["Caption"]));
MyListBox.Items.Add(string.Format("Version: {0}", Object["Version"]));
MyListBox.Items.Add(string.Format("Manufacturer : {0}", Object["Manufacturer"]));
}
{
catch (Exception ex)
{
MyListBox.Items.Add(string.Format("Something is going on..."));
}
}
You can read about async programming in WPF here
It would be awesome if someone can actually put comments where I'm stuck
What you are using is not MVVM as you think. You also need get more knowledge about threading
is MVVM necessary for responsive UI? Can I just implement async/await and
be done with it like I'm trying to do in the example I have below?
MVVM is not necessary. You can use async/await. They are not related to each other
Although I get the results I want, the GUI is unresponsive
Your GUI is unresponsive because you are doing your tasks in UI thread. By calling this.Dispatcher.Invoke you are saying that you want code inside to be executed in Dispatcher thread (which is actually responsible for handling UI)
When building a Coded UI Map, I specify the application that needs to be launched as shown below.
When I run the following test, the Coded UI Test passes, having been able to locate the controls I'm specifying. In this case, it's a ListViewItem.
[TestMethod]
public void UserOpensAnExistingDiary()
{
this.UIMap.OpenExistingDiary();
}
public void OpenExistingDiary()
{
#region Variable Declarations
WpfListItem uIPenAppsLogicModelsDiListItem = this.UIPENWindow.UIDiariesGroup.UIItemList.UIDiaryGroup.UIPenAppsLogicModelsDiListItem;
WpfWindow uIDiaryEditorWindow = this.UIDiaryEditorWindow;
#endregion
// Launch '%LOCALAPPDATA%\Pen\app-5.0.6018.18517\Pen.Apps.Desktop.exe'
ApplicationUnderTest penAppsDesktopApplication = ApplicationUnderTest.Launch(this.OpenExistingDiaryParams.ExePath, this.OpenExistingDiaryParams.AlternateExePath);
// Double-Click 'Pen.Apps.Logic.Models.DiaryModels.Diary' list item
Mouse.DoubleClick(uIPenAppsLogicModelsDiListItem, new Point(76, 72));
// Wait for 1 seconds for user delay between actions; Click 'Diary' window
Playback.Wait(1000);
Mouse.Click(uIDiaryEditorWindow, new Point(590, 25));
}
If I delete the Launch UI Action, and programmatically launch the app the test is unable to locate the ListViewItem. The only difference is my removing the Launch action, and adding the following code to my tests, so they're initialized with the window launched.
[TestInitialize]
public void Setup()
{
string appPath = ApplicationPath.GetApplicationPath();
var app = ApplicationUnderTest.Launch(appPath);
}
Does anyone know why this would be the case?
The examples you provided are confusing as to what works and what doesn't. Also, using the UI maps makes it extremely difficult to see what is going on. Please add one of the test methods that is failing and include the UI Map code for
this.UIPENWindow.UIDiariesGroup.UIItemList.UIDiaryGroup.UIPenAppsLogicModelsDiListItem
My hunch would be that the application under test is not being used as a search limiting container in the failing case.
What I would do, is change to something like:
[CodedUITest]
public class TestingClass
{
WpfWindow containingWindow;
[TestInitialize]
public void Initialize()
{
this.containingWindow = ApplicationUnderTest.Launch(appPath);
}
[TestMethod]
public void Test1()
{
WpfListItem toClick = new WpfListItem(this.containingWindow);
// look in the UI map to see what it is doing for search properties
// and take the simplest sub-set that makes sense
toClick.SearchProperties.Add("AutomationId", "SomeId");
Mouse.Click(toClick); // do not need point, typically
/*
//You may need to include more levels of searching,
//but you can see what you need from the UI Map
WpfTable table = new WpfTable(this.containingWindow);
table.SearchProperties.Add("AutomationId", "myTableId");
WpfListViewItem itemToClick = new WpfListViewItem(table);
itemToClick.SearchProperties.Add("Name", "Some name");
*/
}
}
The point here is that the list item is getting the launched window as it's container which seems to not be happening in your current case.
Ok so I'm attempting to create a simple game. In a nutshell it's a resource management game where the player will attempt to manage a thieves guild. In regards to running missions I've created a Thief class, a new instance of which is created when a new thief is recruited. I have coded within the thief class the ability to gain experience and level up.
Here's my specific problem:
I want the player to be able to select which thief/thieves to send on a mission. I have thought about it and figured that opening a new form and populating it with checkboxes is the easiest way to allow this. These checkboxes will be related to a List<thief> of thieves, the player then checks the thieves s/he wants to send and these are then stored in another List<thief> and passed on to the run mission function.
I've built a separate project with the intention of testing and playing around with this before putting it into the main program. The test project consists of two forms: The first (frmMain) with a textbox to hold the selected options and a button to open the second form (frmSelect). Currently I can open and populate the second form (frmSelect) but when I try to add the checked options to the textbox I simply...well can't.
So far I have tried directly accessing the textbox by typing frmMain.txtOptionsDisplay in the cs file of frmSelect but it causes the following error:
An object reference is required for the non-static field, method or
property
I tried to create a new form in frmSelect and make it equal to the active instance of frmMain with: Form frmTemp = frmMain.ActiveForm; and then alter the textbox using frmTemp as a go-between but that produced the error:
'System.Windows.Forms.Form' does not contain a definition for
'txtOptionsDisplay'.
Having searched both google and stackoverflow forums I've encountered answers that I either have never heard of (Threading) or answers that I kind've recognise but can't interpret the code pasted to make it relevant to my problem (delegates).
Any advice or pointers would be fantastic.
EDIT:
frmMain code:
public frmMain()
{
InitializeComponent();
selections.Add("Option 1");
selections.Add("Option 2");
}
private void btnClick_Click(object sender, EventArgs e)
{
frmSelectOptions.Show();
int length = selections.Count();
for (int i = 0; i < length; i++)
{
CheckBox box = new CheckBox();
box.Text = selections[i];
box.AutoSize = true;
box.Location = new Point(50, 50*(i+1));
frmSelectOptions.grpControls.Controls.Add(box);
}
}
public void updateText(string option)
{
txtOptionsDisplay.Text += option;
}
}
frmSelect code:
public List<CheckBox> selectedOptions = new List<CheckBox>();
Form frmTemp = frmMain.ActiveForm;
public frmSelect()
{
InitializeComponent();
}
private void btnSelect_Click(object sender, EventArgs e)
{
foreach (CheckBox box in grpControls.Controls)
{
if (box.Checked == true)
selectedOptions.Add(box);
}
this.Hide();
}
}
I hope this formats correctly... I'm kinda new and don't know how to indent. Oh look there's a preview...
Does this help?
Your problem is that controls defined within a form by default receive the private access identifier. Hence you could just add a property along the lines of
public ControlType ProxyProperty {
get {
return txtOptionsDisplay;
}
}
Besides from that you should think about wether what you're trying is actually a good solution. Manipulating forms from one to another will become a huge clusterfuck in terms of maintenance later on.
I'd suggest using the Singleton pattern for your frmMain. This will help safeguard you from accidentally launching another instance of frmMain and at the same time, will give you access to frmMain's objects. From there, you can either write accessors to Get your txtOptionsDisplay or you can make it public. Below is an example:
public class frmMain
{
private static frmMain Instance = null;
private static object LockObj = new object();
public static frmMain GetMain()
{
// Thread-safe singleton
lock(LockObj)
{
if(Instance == null)
Instance = new frmMain();
return Instance;
}
}
public string GetOptionsDisplayText()
{
return txtOptionsDisplay.Text;
}
}
public class frmSelect
{
private void frmSelect_Load(object sender, EventArgs e)
{
// Set whatever text you want to frmMain's txtOptionsDisplay text
txtDisplay.Text = frmMain.GetMain().GetOptionsDisplayText();
}
}
If you do go this route, don't forget to update Program.cs to use frmMain's singleton.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new frmMain()); - Old method
Application.Run(frmMain.GetMain());
}
I am having following scenario that I need to show preview option in my application like what ms-word does. When we click the info option under File menu item, then preview of document is shown.
In the same way, I also want to show the preview of my data rendering part in my application when someone clicks File\Info panel. For this i have written a method which gets the preview or screenshots of my app but that method is taking some time So when somebody click on the File menu then application hangs for a while. So, i tried to call that method on different thread using background worker as well as normal thread mechanism. but the thing is that method I am calling on different thread it returns an image source object and when I try to access that object on run worker completed event of background worker, then it shows an exception like owner of this object is a different thread which means that returned image has been created on a different thread therefore I can't use it. So, what is the optimized way to get and use that image in my case.
Code tends to be like this.
public void ShowPreview()
{
ImageSource source =null;
var bgWorkerThread = new BackgroundWorker()
bgWorkerThread.DoWork +=(SENDER,ARGS)=> {
source = planView.GetPreviewImage();
}
bgWorkerThread.RunWorkerCompleted += (sender,args)=>
{
// Application crashes at this point
infoPanel.PreviewImage.source = args.Result as ImageSource;
}
}
you can use invoke or you could create a "storage class" (i think its called a singleton but I'm not sure) reuse the same instance across several classes and/or threads like this.
class Test
{
void main()
{
newThread nt = new newThread();
Storage store = new Storage();
nt.store = store;
Thread t = new Thread(new ThreadStart(nt.runMe));
t.Start();
}
}
public class newThread
{
public Storage store;
public void runMe()
{
store.someNum = 8;
}
}
public class Storage
{
public int someNum;
}
ladies and gentlemen, once again I unfortunately am going to bother you with newbie stuff. I have searched for this information for hours, so if there’s a thread with what I want on it, it’s buried deeper than I could find.
This was my first question here, and Mark Hall was kind enough to set me straight. Since then, I have created a new project and recreated my first three screens as user controls – a container/login, a choice screen, and a main screen (currently empty). If a user has more than one collection, the choice screen pops up and allows them to choose a collection.
I did run into a snag with parameters, but I’m solving that by overloading the form declaration (solution found here) – yes, I know it’s much better to send parameters through calls, but I’d hate to have to create a call for each parameter (do I?) and… OK, OK, I’m better at this than {get, set}. Man, I hate being a newbie.
Anyways, I’m having trouble with the choice form – I can’t seem to call it, close it, then go to the main form. I have no problem (if there’s only one collection) in going straight to the main form, it’s that darn choice form. Yes, I know I could include a choice datagridview, but a few of our end users aren’t the sharpest bulb in the tool shed, and need hand-holding. Anyways, here’s the code.
CONTAINER/LOGIN SCREEN
namespace DeleteThis
{
public partial class ContainerForm : Form
{
Main Main = new Main();
LoginCollectionChoice LoginChoice = new LoginCollectionChoice();
DataTable dtPermissions = new DataTable();
public ContainerForm()
{
InitializeComponent();
Main.ExitEvent += new Main.ExitEventHandler(Main_ExitEvent);
LoginChoice.ExitEvent += new
LoginCollectionChoice.ExitEventHandler(LoginChoice_ExitEvent);
}
void LoginChoice_ExitEvent(object sender, EventArgs e)
{
pnlcontainer.Controls.Remove(LoginChoice);
}
void Main_ExitEvent(object sender, EventArgs e)
{
pnlcontainer.Controls.Remove(Main);
}
private void btnLogin_Click(object sender, EventArgs e)
{
LoginProcedure();
}
private void LoginProcedure()
{
DataTable dtPermissions = AdminClass.GetCollectionsForUser(int.Parse(txtbxUserName.Text));
if (dtPermissions.Rows.Count == 1)
{
//Valid user, one collection. Head right in.
pnlcontainer.Controls.Add(Main);
Main.BringToFront();
}
else
{
//More than one collection found. Giving user choice
LoginCollectionChoice LoginChoice = new LoginCollectionChoice(dtPermissions);
pnlcontainer.Controls.Add(LoginChoice);
LoginChoice.BringToFront();
}
}
private void btnExitProgram_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
I hope I didn't kill anything in the snipping. And now the choice screen…
public partial class LoginCollectionChoice : UserControl
{
public delegate void ExitEventHandler(object sender, EventArgs e);
public event ExitEventHandler ExitEvent;
private static DataTable dtPermit;
DataTable dtPermissions = new DataTable();
public LoginCollectionChoice()
{
}
public LoginCollectionChoice(DataTable dtPermissions)
{
InitializeComponent();
GrdCollection.DataSource = dtPermissions;
dtPermit = dtPermissions;
}
private void btnChoose_Click(object sender, EventArgs e)
{
//Code for the user to choose a collection
ExitEvent(this, new EventArgs());
}
}
I’ve snipped all non-relevent code, I hope you gentlemen and ladies can help this newbie get on the right path. Please, be gentle, you wouldn’t want to see me cry, would you? :) Oh! And if you know of any great tutorial sites, please email them to me. I'd prefer to spend a week on tutorials than a week on stumbling and asking here. Thank you all very much.
Are you calling logonForm.Show()?
It seems like you'd need to show it that way.
You use BringToFront(), but I think it needs to be shown first.
The first thing that jumps out to me is that you are calling
LoginCollectionChoice LoginChoice = new LoginCollectionChoice(dtPermissions);
which is creating a local variable called LoginChoice, which is not the same as your class variable even though they share the same name.
What you want to do is to not declare a local variable in that method. Change that line to
LoginChoice = new LoginCollectionChoice(dtPermissions);
Having said that, I believe tylerjgarland in that you need to call .Show() first. And the way you are closing the form is certainly odd. I would create a form, showDialog, get the result and then close the form that way.