Run code after multiple events completed - c#

I have an event set up to fire when an item in a ListView is checked. The event calls a function which updates various controls inside my form. Among other things, I need to enable or disable buttons based on how many items are checked. This function is quite expensive.
Example:
private void listView_ItemCheck(object sender, ItemCheckEventArgs e)
{
UpdateForm();
}
Now the problem arises when a user wants to check many items at once. This causes the application to be unresponsive for a little while.
So, I would like to call UpdateForm() once after all the items were checked, instead of every time a single item is checked.
EDIT:
Here's a part of UpdateForm():
private void UpdateForm()
{
// Puts all files in the mod list in a new list
List<string> modListFiles = new List<string>(lvFiles.Items.Count);
foreach (ListViewItem lvi in lvFiles.Items)
{
modListFiles.Add(lvi.Text);
}
// Adds found files to the file list
foreach (string file in Files)
{
lvFiles.Items.Add(new ListViewItem(file));
}
// Removes files from mod list that no longer exist
List<string> deleteQue = new List<string>(lvFiles.Items.Count);
foreach (string file in modListFiles)
{
// If a file in the list doesn't exist anymore, que it to delete
if (!Files.Contains(file))
{
deleteQue.Add(file);
}
}
// Remove queued files
foreach (string file in deleteQue)
{
foreach (ListViewItem lvi in lvFiles.Items)
{
if (lvi.Text == file)
{
lvFiles.Items.Remove(lvi);
break;
}
}
}
// Grays out mod list if a profile is installed
if (InstalledProfile == null)
{
lvFiles.BackColor = SystemColors.Window;
lvFiles.ForeColor = SystemColors.WindowText;
}
else
{
lvFiles.BackColor = SystemColors.Control;
lvFiles.ForeColor = SystemColors.ControlDark;
}
// Fills out the game path if it exists
if (Directory.Exists(GamePath))
{
txtGamePath.Text = GamePath;
}
else
{
txtGamePath.Text = "Game directory does not exist!";
}
// Makes sure that the cbxProfiles_SelectedIndexChanged doesn't run UpdateForm() again
handleProfileChanged = false;
// Adds profiles to the combobox
foreach (string profile in Profiles)
{
if (!cbxProfiles.Items.Contains(profile))
{
cbxProfiles.Items.Add(profile);
}
}
// Removes nonexistant profiles from the combobox
foreach (string profile in cbxProfiles.Items)
{
if (!Profiles.Contains(profile))
{
cbxProfiles.Items.Remove(profile);
}
}
if (InstalledProfile == null)
{
btnInstallUninstall.Text = "Install";
}
else
{
btnInstallUninstall.Text = "Uninstall";
}
if (Directory.Exists(GamePath) && lvFiles.CheckedItems.Count > 0)
{
btnInstallUninstall.Enabled = true;
}
else
{
btnInstallUninstall.Enabled = false;
}
}
I've had to simplify some things so please excuse any errors I have probably made.
Some context:
I'm trying to make a program that copies files from a set directory \mods to a user specified directory GamePath. It shows all files found in \mods, then allows the user to check some of them. Clicking btnInstall will copy these files to GamePath. After this so called installing, the copied files can be removed by clicking btnInstall again.
All of the properties I made (Profiles, GamePath) get and set their values using an XML file on disk. The main ListView is called lvFiles, sometimes called mod list or file list in comments.

I've managed to speed the process of checking files up considerably by not calling UpdateForm(). Instead, I made a function UpdateButtons() that only enables/disables buttons.
This way, we do not call UpdateForm() until either the form is activated or the process exits.
Although the current code is far from perfect, all of your help was very useful and is greatly appreciated. I will probably think about the updating mechanism a bit more and apply some good threading later on.
Thank you all!
Here is the code if you want to see it:
private void UpdateButtons()
{
#region btnOpenPath
if (Directory.Exists(GamePath))
{
btnOpenPath.Enabled = true;
}
else
{
btnOpenPath.Enabled = false;
}
#endregion
#region btnInstallUninstall
if (InstalledProfile == null)
{
btnInstallUninstall.Text = "Install";
}
else
{
btnInstallUninstall.Text = "Uninstall";
}
if (Directory.Exists(GamePath) && lvFiles.CheckedItems.Count > 0)
{
btnInstallUninstall.Enabled = true;
}
else
{
btnInstallUninstall.Enabled = false;
}
#endregion
#region btnDelete, btnCheckAll, btnUncheckAll
if (InstalledProfile == null)
{
btnDelete.Enabled = true;
btnCheckAll.Enabled = true;
btnUncheckAll.Enabled = true;
}
else
{
btnDelete.Enabled = false;
btnCheckAll.Enabled = false;
btnUncheckAll.Enabled = false;
}
#endregion
}

Related

How to select a GuiComboBox Entry in SAP

I want to script our sap. Actually, I'm scripting the comboboxes. But I don't know, how to select a specific item.
SAPFEWSELib.dll included as refernece
public static bool SelectComboBoxItem(string ItemText)
{
int i = 0, ItInd = -1;
SAPFEWSELib.GuiComboBox GCB = GetComboBox(GFW, Criteria, Type); /*This function returns the SAPFEWSELib.GuiComboBox and works correctly*/
if (GCB != null)
{
foreach (SAPFEWSELib.GuiComboBoxEntry Entry in GCB.Entries)
{
if (Entry.Value.ToUpper().IndexOf(Item.ToUpper()) != -1)
{
/*How to select this Entry?*/
/*GCB.Entries.Item(Entry.Pos).Select() is a not contained methode*/
/*GCB.Entries.Item(Entry.Pos).Selected = true This functions for GuiTableRows and GuiGridViewRows, but not here*/
return true;
} else {
i++;
}
}
}else{
throw new System.Exception("ERROR: Unable to find combobox with current criteria!");
}
return false;
}
Does anybody has an Idea?
Ok, got it.
GCB.Value = Entry.Value;
In my testcase, the combobox was not changeable, so it never functioned.

How to stop Custom OnSaving event after first saving in SItecore?

I am trying to stop my custom OnSaving event after it has been applied to the first item in the save chain.
but so far I have not been able to, and I end up with a stackoverflow exception.
Is there a simple way of doing this ?
Best regards,
Robin
private void AddOrRemoveRedirectingItemIdFromSavingItemIdList(Item savingItem, SitecoreEventArgs sitecoreEventArgs)
{
ItemLink[] referers = Globals.LinkDatabase.GetReferrers(savingItem);
var guidList = new List<ID>();
foreach (ItemLink link in referers)
{
// checking the database name of the linked Item
if (!link.SourceDatabaseName.Equals(Context.ContentDatabase.Name, StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
Item item = Context.ContentDatabase.Items[link.SourceItemID, savingItem.Language];
// adding the Item to an array if the Item is not null
if (item == null || item.Fields["301Redirect"] == null || item.Fields["301RedirectedTo"] == null)
{
continue;
}
// Update the saving item ids
CheckboxField redirectField = item.Fields["301Redirect"];
if (redirectField.Checked)
{
guidList.Add(item.ID);
}
}
if (guidList.Any())
{
this.SaveIDsToEditingItem(savingItem, guidList, false);
}
}
private void SaveIDsToEditingItem(Item editingItem, IEnumerable<ID> guidList, bool forceModified)
{
Field redirectedToFromItemId = editingItem.Fields["301RedirectedToFromItemId"];
using (new EditContext(editingItem))
{
// Saving the redirected items ids
string redirectedToFromItemIdOld = redirectedToFromItemId.Value;
string redirectedToFromItemIdNew = string.Join("\n", guidList.Select(guid => guid.ToString()));
// if the values are not changed
if (redirectedToFromItemIdNew.Equals(redirectedToFromItemIdOld))
{
return;
}
redirectedToFromItemId.Value = redirectedToFromItemIdNew;
if (forceModified)
{
editingItem.RuntimeSettings.ForceModified = true;
}
}
}
}
You can do this 2 ways. The better way would be to remove the using (new EditingContext(editingItem) section from the SaveIDsToEditingItem. In the OnItemSaving event, any changes made to the savingItem would be kept.
Alternatively, if you need to use the editing context for some reason you need to use an EventDisabler in your SaveIDsToEditingItem method:
private void SaveIDsToEditingItem(Item editingItem, IEnumerable<ID> guidList, bool forceModified)
{
Field redirectedToFromItemId = editingItem.Fields["301RedirectedToFromItemId"];
using (new EventDisabler())
{
using (new EditContext(editingItem))
{
// Saving the redirected items ids
string redirectedToFromItemIdOld = redirectedToFromItemId.Value;
string redirectedToFromItemIdNew = string.Join("\n", guidList.Select(guid => guid.ToString()));
// if the values are not changed
if (redirectedToFromItemIdNew.Equals(redirectedToFromItemIdOld))
{
return;
}
redirectedToFromItemId.Value = redirectedToFromItemIdNew;
if (forceModified)
{
editingItem.RuntimeSettings.ForceModified = true;
}
}
}
}
This will prevent the OnSaving event from being fired again.

How I can realize parallel search in ObservableCollection?

I have an ObservableCollection, where Item has 2 properties(for example: Name and Id) and collection contains of 12k elements. So, i have a textbox, i want to search elements, which names contains my textbox value and add these elems in new collection.
in real-proj:
Silverlight, TreeView(its ItemSource is my collection) which dynamically changing. And TreeView changing in UI.
My problem is just in slowly rendering results of search. I thing if it'll be parallel - it saves me.
for example, some code im using:
private ObservableCollection<ICDDocumentItemViewModel> LinearSearch(string searchText)
{
var filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
if (searchText.Length > 3)
{
foreach (var itemViewModel in _linearCollection)
{
if (!itemViewModel.Model.Name.ToLower().Contains(searchText.ToLower())) continue;
if (itemViewModel.Children.Count != 0)
{
itemViewModel.IsExpanded = true;
}
filteredCollection.Add(itemViewModel);
}
}
if(searchText.Length <= 3)
{
return new ObservableCollection<ICDDocumentItemViewModel>(ICDItemsViewModelsMain);
}
return filteredCollection;
}
there is no need to have parallel processing in place normally, this code should help you here.
private ObservableCollection<ICDDocumentItemViewModel> GetFiltered(string filter)
{
ObservableCollection<ICDDocumentItemViewModel> filteredCollection;
if (filter.Length > 3)
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>(_linearCollection.Where(x => x.Name.ToLower().Contains(filter)));
filteredCollection.ToList().ForEach(DetectChildren);
}
else
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
}
return filteredCollection;
}
private void DetectChildren(ICDDocumentItemViewModel item)
{
item.IsExpanded = item.Children.Any();
}

Debugging a windows form application

Im making a chat program that saves the messages in files like pub0.zzc, all computers that are using it will be connected to the hard drive that these files are in, so its fine. The method data.Chat.Read(MessageTypes type, string Channel) infinite loops through a try catch statement till it returns the messages. I used this before and works perfectly. But, my code was hard to manage so instead of just putting text boxes into the window and using the code each time, i created a user control (MessageViewer). It works fine, once again when I run it, BUT it freezes VS whenever I try to use the designer on the window housing the control. the probelm isnt the window because when i delete the control its fine. I think the possible errors are at RefreshMessages() and the Refresher_Tick(...)
Refresher.Stop() and .Start() is also not it, worked fine before
so here is the code:
private void Refresher_Tick(object sender, EventArgs e)
{
Refresher.Stop();
int RefreshRate = 4;
bool Live = true;
if (RefreshRateChoice == "Manual")
{
Live = false;
RefreshRate = 1;
}
else if (RefreshRateChoice == "4 ( Default )")
{
Live = true;
RefreshRate = 4;
}
else
{
Live = true;
RefreshRate = Convert.ToInt32(RefreshRateChoice);
}
if (data.Chat.Read(MessageType, ChannelChoice) != ContentPresenter.Text && Live)
{
RefreshMessages();
}
Refresher.Interval = RefreshRate;
Refresher.Start();
}
public void RefreshMessages() {
if (data.Chat.Read(MessageType, ChannelChoice) != ContentPresenter.Text)
{
ContentPresenter.Text = data.Chat.Read(MessageType, ChannelChoice);
}
}
and if you need it:
public static string Read(MessageTypes Type, string Channel)
{
string Loc;
if (Type == MessageTypes.Public && (Channel == "1" || Channel == "2"))
{
return "Can not view this channel, only post to it.";
}
if (Type == MessageTypes.Public)
{
Loc = data.AssetsFolder + "\\pub" + Channel + ".zzc";
}
else if (Type == MessageTypes.Private)
{
Loc = data.AssetsFolder + "\\" + Channel + ".zzpc";
}
else if (Type == MessageTypes.Game)
{
Loc = data.AssetsFolder;
}
else
{
Loc = data.AssetsFolder;
}
while (true)
{
try
{
String MessageList = "";
StreamReader MessageReader = new StreamReader(Loc);
string EncMessages = MessageReader.ReadToEnd();
MessageReader.Dispose();
List<string> EncMsgList = EncMessages.Split(';').ToList();
for (int i = 1; i < EncMsgList.Count; i++)
{
MessageList += data.Encodings.Decrypt(EncMsgList[i], Palettes.Message) + "\n";
}
return MessageList;
}
catch
{
// Do nothing
}
}
}
You say that it "freezes."
In your Read method you have a while(true) loop with an embedded try...catch block, but the catch never returns you from that method. If you keep throwing the same exception, you'll continue to loop over and over which could be where you are freezing.
At least to prove that is the case, put a return in you catch or some diagnostic code to indicate if that is the case.

Search in ListView c#

I've wrote a method to search thru ListView for a given string and mark the ones that are found with Color. It works fine however with lots of information on screen and scrollable ListView it's sometimes hard to find what user is looking for.
Normally I do create special searches by modifying method and SQL query WHERE clause but it's always a pain and requires more work/code for each ListView/Data.
I would like to have some generalized search that would work for all kind of searches in ListView just like one I have now but with ability to hide (rows) what's not needed and show only necessary rows. Of course if one changes it's mind it has to bring back the old rows back.
I guess the biggest problem for me is how to store all the columns and data without over complicating knowing that it can be 3 to 20+ columns and multiple rows.
public static void wyszukajNazweListView(ListView varListView, string varWyszukaj) {
if (varWyszukaj != "") {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
string textToAdd2 = drv.Text;
if (textToAdd2.Length >= 1) {
if (textToAdd2.ToLower().Contains(varWyszukaj.ToLower())) {
drv.BackColor = Color.DarkOrange;
} else {
drv.BackColor = Color.White;
}
}
}
bool varColor = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
if (drv.BackColor == Color.DarkOrange) {
varColor = true;
break;
}
}
if (varListView.SmallImageList != null) {
if (varColor) {
comp.ImageIndex = 2;
} else {
comp.ImageIndex = -1;
}
}
}
} else {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
comp.BackColor = Color.White;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
drv.BackColor = Color.White;
comp.ImageIndex = -1;
}
}
}
}
I'd probably store it as a DataTable object. DataTable type allows setting its rows as hidden (e.g. Visible = false) and you can bind your ListView directly to it.
EDIT: noticed the WinForms tag. Even simpler: no need to mock about with ViewState/Session.

Categories