I have an AutoCompleteBox binded to an ObservableCollection ItemsSource which I filter on my own by querying entities from a domainservice.
I used the scenario of populating from a webservice call from the blog of Jeff Wilcox, by setting the PopulatingEventArgs.Cancel to True, and when my collection is ready, I call PopulateComplete() on the ACB.
My goal is to reopen the dropdown on mouseover (or click) but without reloading again all the data from the web. I found a question on stackoverflow where the answer was to set IsDropDownOpen to True. But in this case, the ACB population starts again, and another call goes to the webservice.
Of course, when the user starts typing, the filtering should be done again.
(for ex. you type "ric" and the box suggests "rice" and "ricin", you select rice, but you change your mind and want to select another one from the same collection, lets say "ricin". In this case you already have the suggestions containing "ric" in memory, no need to load them again..)
I found an alternative way in which instead of setting IsDropDownOpen, I just simply call the PopulateComplete() method. This does exactly the same thing that I want, but with a little fail: after my ACB loses focus, the dropdown is not opened again on mouseover liek it should. Even when I click back into the acb textbox.
So is there a fix for this, or does someone know why the PopulateComplete() only reopens the dropdown when the ACB has focus for the first time? Or this was only my luck that calling this method reopened the dropdown and the IsDropDownOpen property should be used instead (afaik this would be only possible with some flags indicating that its a fake populating event triggered by my mouseover and after PopulatingEventArgs.Cancel i should call immediately PopulateComplete. but i dont get it, if this may work (haven't tried yet), why not when calling simply the PopulateComplete)?
Well, I tried the IsDropDownOpen with a testing bit, and almost worked:
private void FoodBox_MouseEnter(object sender, MouseEventArgs e)
{
//FoodBox.PopulateComplete(); not working after acb loses focus...
testbit = true;
FoodBox.IsDropDownOpen = true;
}
Here's the overloaded Populating method (no need for setting ItemsSource explicit because its bound to an ObservableCollection):
public void FoodBox_Populating(object sender, PopulatingEventArgs e)
{
e.Cancel = true;
if (!testbit)
{
VM.LoadFoodSuggestions(FoodBox.SearchText);
}
else
{
testbit = false;
FoodBox.PopulateComplete();
}
}
This works good so far, execpt that the search does not start because when (for the first time) you mouseover and select the acb, it sets the testbit to true.
So I added another event handler that takes care of setting the testbit to false every time the user inputs text on the keyboard, ensuring that the suggestions are regenerated/reloaded after SearchText is modified by the user, but not when you select an item from the dropdown:
private void FoodBox_TextChanged(object sender, RoutedEventArgs e)
{
testbit = false;
}
I still don't know why calling PopulateComplete() isn't enough without setting the IsDropDownOpen to Ture, and setting that to true, also delays the dropdown opening approximately with the time specified in the MinimumPopulateDelay, but at least it gives me the functionality I wanted. (Maybe digging into the source of acb would answer this mistery)
Maybe this functionality implemented in the basic acb would be helpful in a future release of the control.
Related
I'm refactoring a body of code, looking through it all, line by line.
I came across an event handler:
private void mnuUpdate_Click(object sender, EventArgs e)
...and, not recognizing which menu item called this (the menu item names do not always match their labels, or even come close), was curious.
The main menu on the form has no such menu item among its children.
I r-clicked the event handler, selected "Find Usages*" and was led here:
this.mnuUpdate.Text = "Update";
this.mnuUpdate.Click += new System.EventHandler(this.mnuUpdate_Click);
(This is an antedeluvial app that predates .NET's partial class goodness, so this is in the same file)
On the form in the designer, when I select "mnuUpdate" from the properties page combobox, the mainMenu on the form disappears altogether.
How can I track down this fugitive menu item? There is no popupMenu or contextMenu on the form, just the mainMenu control...???
The only other usage is:
if (ResetConnectionFetchForm)
mnuUpdate_Click(sender, e);
Is it possible that this is simply a phantom menu item that should be converted into a "regular old" method?
UPDATE
As the most intelligent George used to say, "Curiouser and Curiouser." Now I find this:
public void btnCancel_Click(object sender, EventArgs e)
...and though it is called from seven places in the code, there is no btnCancel on the form...It is a "fake" button click event. Oh my Lanta!!!
So, I replaced it with a parameterless private method with the exact same code (it didn't use either sender or event args).
If the cat who wrote this cockamamie glob of fruitcake-battered spaghetti was deliberately trying to drive the next cat (me) crazy, it's working pretty well, and would make a good Poe-style story or Hitchcock-style flick.
...I see...Dead Code!!!
Okay, mystery solved. mnuUpdate is dynamically added (conditionally) to mnuSetup (which is a top level menu item with the Text property "Fetch") like so:
if (!mnuSetup.MenuItems.Contains(mnuUpdate))
{
mnuSetup.MenuItems.Add(mnuUpdate);
UpdateMenuItemSelectable = true;
}
I reckon selecting mnuUpdate from the combobox in the form's Properties page is because there is no visual representation to show at that point.
Selecting "mnuSetup" highlights the "Fetch" menu item, but selecting "mnuUpdate" causes it all to scurry away faster than cockroaches from the light.
So the bizarre thing about it now is: why is the menu item not dynamically created as necessary, instead of being explicitly created and then dynamically added; seems like a strange way for a cat to skin a cat.
I'd suggest you turn it into "regular old menu" so someone else doesn't waste time figuring it out.
Me - I would have thought it obsolete code because it doesn't have a Handles clause.
You can use .Visible and .Enabled to control what the user sees.
In my windows application i have a context menu with a grid the problem is that I want to disable the ToolStripMenuItem in context menu according to the user previlages.How can i do that. i have done like this but it is not working
private void contextMenuStrip_Machine_Opening(object sender, CancelEventArgs e)
{
toolStripAuthorize.Enabled = INFOpermission.accessAuthorize;
}
but it is not working
You need to set toolStripAuthorize.Enabled to either true or false.
I have no idea what INFOpermission.accessAuthorize is because you didn't show the code that defines that (enum?), but if it's anything other than false, this isn't going to work out like you expect.
I can guarantee that setting the Enabled property of the ToolStripMenuItem that you want to disable to false in the Opening event handler will work. If it's not working for you, you're doing something else wrong, and you need to give us some more information to go on.
If you're stuck, see the sample code here: How to: Handle the ContextMenuStrip Opening Event
EDIT: Armed with new information provided in the comments, I've now isolated the source of the problem. You've assigned the ContextMenuStrip to the RowTemplate of a DataGridView control, and are therefore not able to modify items contained in that context menu in its Opening event handler method.
It turns out that this is a known bug that someone decided was "by design". You can see the original bug report here on Microsoft Connect. The explanation given is that whenever a new row is created based on the RowTemplate (which is how the RowTemplate works), the ContextMenuStrip that you've assigned gets cloned as well. That means the same context menu instance is not used for each row, and whatever properties that you try to set on the original menu items have no effect.
Fortunately, it also gives us a workaround. Like all events, the Opening event passes the actual instance of the ContextMenuStrip that is about to be opened as its sender parameter. This is the context menu whose items you need to modify in order for your alterations to be visible.
So what's the code? It looks like this:
private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
{
ContextMenuStrip cmnu = (ContextMenuStrip)sender;
cmnu.Items[1].Enabled = false;
}
Notice, though, that you'll have to reference the individual menu item that you want to modify by its index. This is just the zero-based position of the item in the menu that you want to modify. You can't use the toolStripAuthorize object like you were trying to do before because a new instance of it has been cloned for each new context menu instance.
My scenerio is like this:
At runtime, I bind ToolStripComboBox to array of struct:
cbxTimes.ComboBox.DataSource = PlayTimeLengths;
cbxTimes.ComboBox.DisplayMember = "Description";
cbxTimes.ComboBox.ValueMember = "Minutes";
The DropDownStyle of ToolStripCombobox is set to DropDown.
Everything is working fine, I can select values from the dropdown list and I can write text in the control.
However I wanted to prevent user from pressing some controls and alternate the Text property when some other controls are pressed.
I am trying to accomplish this in KeyPress event:
private void cbxTimes_KeyPress(object sender, KeyPressEventArgs e)
{
var cbxSender = ((ToolStripComboBox)sender).ComboBox;
string S = cbxSender.Text;
//some operations on the S variable
cbxSender.Text = S;
e.Handled = true;
} // breakpoint here shows that cbxSender.Text is not changed to S!
So the Text property has not been changed but I didn't get any exception.
However, if I run the program further (I quit from the debugging) I see that the Text property is changed - to be more specific. I see the text from S inside the control.
Now, imagine that I press any key for the second time, and again I am in the debugger in the same event:
private void cbxTimes_KeyPress(object sender, KeyPressEventArgs e)
{
var cbxSender = ((ToolStripComboBox)sender).ComboBox;
string S = cbxSender.Text; // this time breakpoint is here
//some operations on the S variable
cbxSender.Text = S;
e.Handled = true;
} // breakpoint here shows that cbxSender.Text is not changed to S!
But this time I put breakpoint on the second line and after examining the Text property I see that it still has not changed. Despite the fact that I've altered it on the first time when the event was fired up and the altereted text is visible in the control. But under debugger I see different value, I see value that has been set up at the begining. Value which belongs to the array of structs.
SO what can I do to overcome this problem?
Honestly this is one of the things I hate about Windows Forms databinding. In WPF you would not bind to the objects directly, you'd bind to a "ViewModel" object which encapsulated this view logic you have and bind to it instead.
My workaround to all of this would be to just not use databinding for this case at all and manually populate the items as needed. I can understand why you might be having this problem. If you had updated your underlying bound object's .Text (or whatever causes ToString() to display the value), you would probably see the new value you'd set, but that upsets the semantics of your underlying objects, which is Not A Good Thing.
I'm attempting to use the ScintillaNET control in an application I am working on. I drag and drop the control into my form and run form. The control appears on the form. This is good. In addition, if I set any of the properties in the control's properties editor (ConfigurationManager.Language, for example), I am able to type in that language and see syntax highlighting occur.
Where I run into problems is when I attempt to change properties programmatically. For example, I attempt to load text from a file into the form (I'm doing this in the form's Load). The text doesn't display. I also can't seem to show the line numbers or do any other number of tasks (including programmatically change the Language).
Any idea what I may be doing wrong? Even something as simple as the code below doesn't seem to work:
private void scintilla1_Load(object sender, EventArgs e)
{
scintilla1.ConfigurationManager.Language = "xml";
}
Simply add scintilla1.ConfigurationManager.Configure();
private void scintilla1_Load(object sender, EventArgs e)
{
scintilla1.ConfigurationManager.Language = "xml";
scintilla1.ConfigurationManager.Configure();
}
After spending some time playing around with the different events, it appears that I cannot affect the Scintilla control until after it is already visible. Hence, the "Load" event does not let me make any programmatic changes to the control until I've set it visible.
It's a little strange, and seems sort of pointless to me to have the Load event at all, but I just wanted to let everybody know what is happening in case someone else ran into the same problem.
I am trying to load a preferences window for my application and I would like the apply button to initially be disabled, then when a preference is updated, the apply button gets enabled again. I have some controls data bound to a preferences object and what happens is that after the window loads, the combobox events get triggered. Is there any event that is guaranteed to happen dead last after everything is stable?
Here is what my code looks like (the apply button is always enabled after the window loads):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_preferencesData = new PreferencesDataContext();
LayoutRoot.DataContext = _preferencesData;
ButtonApply.IsEnabled = false;
}
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ButtonApply.IsEnabled = true;
}
Is it also interesting to note that this only happens with textboxes and comboboxes, not checkboxes or radiobuttons.
Best solution for simple need
Joseph's answer is the best solution by far for your simple need: Just use data binding and let the data model handle it.
Answer to question as posed
There are more complex scenarios when you really do need control after absolutely everything has finished loading and all events have fired. There is no single event that occurs "dead last", but it is easy to effectively roll your own using the Dispatcher queue.
This is how to do it:
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
{
var x = ComputeSomething(1, 2, 3);
DoSomething(x, "Test");
}));
Everything inside the { } will be executed when WPF finishes everything at a higher priority than ContextIdle, which includes all event handlers, loaded events, input events, rendering, etc.
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be found applied in addition to any element-specific initialization you may define [note: Initialized event not fired for leaves in a logical tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional object tree construction including more constructors and getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is shown. The other steps generally don't happen until a Window is shown, but they can happen earlier if triggered manually.
The Window.ContentRendered event fulfilled my requirements.
I just did kind of the same thing behaviorly in a systray WPF app.
However, I didn't do it using event handling. I simply bound the Enabled property of my button to a property in my ViewModel, and had the property updated whenever I needed the behavior.
You can use ManagedSpy to figure this out on your own.
http://msdn.microsoft.com/en-us/magazine/cc163617.aspx
Setting the DataContext will likely fire the SelectionChanged event, and you can't rely on when exactly it's fired. Some logic checking on what exactly is selected would be more reliable:
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (myComboBox.SelectedItem == null)
{
buttonApply.IsEnabled = false;
}
else
{
buttonApply.IsEnabled = true;
}
}
The reason it's happening afterwards with your code as-is is because the event gets queued on the thread for the UI, so it's up to Windows if it will execute the next line of code in Load, or to handle the other events on the queue.
Not to throw a whole lot of stuff at you that you may or may not be familiar with, but if this is a relatively new codebase, you may want to consider using the MVVM pattern and use Commands instead of the archaic (emphasis mine) eventing model.
Order of Events in Windows Forms
Control.HandleCreated
Control.BindingContextChanged
Form.Load
Control.VisibleChanged
Form.Activated
Form.Shown