In my app, I have a group of 3d objects and they're exposed to the user through a TreeView. When a user selects an item in the TreeView, an SelectedItemChanged event is fired, the corresponding 3d object is set to be selected and is highlighted in the 3d render window. This works fine.
What I'm having trouble with is the reverse. In a section of my code, I programatically set the selected 3d object in the scene. I want to reflect the currently selected object in the TreeView, so I run through the items until I find the corresponding one. But once I get to it, I can't find a way to make the item appear selected without having SelectedItemChanged being called, which is not what I want.
Is there a way to do this?
Thanks!
I take it you want to suppress the code in your event-handler? If so, a common way of doing this is with a boolean flag (or sometimes an int counter):
bool updatingSelected;
void SomeHandler(object sender, EventArgs args) { // or whatever
if(updatingSelected) return;
//...
}
void SomeCode() {
bool oldFlag = updatingSelected;
updatingSelected = true;
try {
// update the selected item
} finally {
updatingSelected = oldFlag;
}
}
Would it be appropriate to remove the TreeView's SelectedItemChanged event handler temporarily, and re-add it once you've performed the necessary operations? I haven't tried it myself, but it's the only other thing I can think of (Marc Gravell beat me to my original answer - I've done THAT before ;) ).
Good luck!
Related
I'm trying to programmaticaly fire a DoubleClick event on a specific item of a Windows Forms ListView, but can't seem to find how to do it.
To further explain it, upon opening a form I need to fire a DoubleClick event on a ListViewItem that's on that form, to open the next form. I can't find where to indicate what row I'm interested in firing that event on.
Well i don't know why you Need this, but on this way you can fire the doubleclick programmaticaly:
//I dont know when u will fire this Event so we create a Dump to call our Method
MouseEventArgs f;
//call the Method
Test_DoubleClicked(sender,e);
private void Test_DoubleClicked(object sender, MouseEventArgs e)
{
// some code
}
I managed to figure out what I was missing. The Listview's selected item needs to be set before firing the event.
While the question seems to be (and in fact it is) XY Problem but IMO it's steel a valid question.
With the same reason which some one likes PerformClick method of a Button someone may like to have a Activate method for an Item to activate the item programattically. It's also useful for test purpose.
Right solution for most cases
Before jumping to Activate method, as a comment on the OP's answer, I should say, while the solution which you shared has solved your problem, but here is the right solution for most cases:
It's better to put the logic in a method like DoSomething(ListViewItem item){/**/}. Then you can reuse the same logic in ItemActivate by calling DoSomething(listView1.SelectedItems[0]). Also in case that in some other part of your code you want to use the same logic along with setting an item as selected, it's enough to set item.Selected = true; and then DoSomething(item).*
As you can see, setting selected item is not responsibility of DoSomething, and also in general it's better to handle ItemActivate rather than relying on double click. ItemActivate will raise on a double click as well.
Activating an Item Programmatically
Here in this post I'll share an extension method for ListViewItem to perform item activation programmatically using a method called Activate, then to activate an Item, it's enough to call item.Activate() method.
using System;
using System.Reflection;
using System.Windows.Forms;
public static class ListViewItemExtensions
{
public static void Activate(this ListViewItem item)
{
if (item.ListView == null)
throw new InvalidOperationException();
var onItemActivate = item.ListView.GetType().GetMethod("OnItemActivate",
BindingFlags.NonPublic | BindingFlags.Instance);
item.Selected = true;
onItemActivate.Invoke(item.ListView, new object[] { EventArgs.Empty });
}
}
I have the TreeView with objects. When I select item of these tree, than other control - ListView displays as its items properties of selected object. I want to save values of properties when in TreeView selection is change to other object.
So, is there a good way in WPF to gets values of "just before changing" items in ListView control? My idea for now is to override the PreviewMouseDown to check if user click tree node. By god way I mean better than mine. Maybe something in ListView template?
Indication that there is no need to change my idea with the PreviewMouseDown will be also good answer.
Could you please provide the relevant code snippets? I try to answer your question, but I'm not sure I understood it correctly.
If you bind the SelectedItem of you TreeView to a property (a.e. using MVVM pattern), you can save the values before actually setting the item.
Doing so in the setter is not so good though, because it becomes quite large then. I would have a setter like this:
private Foo bar;
public Foo Bar
{
get { return bar; }
set
{
OnPropertyChanging("Bar");
bar=value;
OnPropertyChanged("Bar");
}
}
Then you can listen to your own PropertyChanging events and do your stuff there:
private void this_PropertyChanging(object param, PropertyChangingEventArgs e)
{
switch(e.PropertyName)
{
case "Bar":
//Do you stuff
break,
}
}
Is there a way to make a control dependent on another control? I have a combo box and a button, and I need the button to be enabled if and only if there is an item selected in the combo box.
I know I can set the Enabled property of the button inside the SelectedIndexChanged callback, but then it will require some code, and besides there's an issue with what initial state the button would have. So I'm looking for something that wouldn't require manually handing events, is this possible?
Thanks!
No, there is no way in winforms to do this without code. What I usually do is to collect all such state-setting code into one specific method:
private void SetControlStates()
{
theButton.Enabled = theComboBox.SelectedIndex >= 0;
// code for other controls follow here
}
Then I trigger this method from all over the place, as soon as there is an interaction that may lead to the state changing (including the last thing I do when the form has finished loading; that takes care of initial state). If you want to avoid unnecessary assignments, just add code to check the value first:
private void SetControlStates()
{
bool buttonEnabled = theComboBox.SelectedIndex >= 0;
if (theButton.Enabled != buttonEnabled) theButton.Enabled = buttonEnabled;
// code for other controls follow here
}
I want to use a CheckedListBox in an application where each item in the ListBox is the name of a folder on my hard drive and for the purpose of reading and writing text files to and from each of these folders I want to ensure that one and only one item (a folder) can be selected at any one time in the CheckedListBox
How can I achieve this via code in C#?
Thanks for reading :-)
Edit \ Update - 22/10/2010
Thanks to all who took the time to reply - especially Adrift whose updated code as requested is working perfectly.
I do appreciate what some commentators said about my usage of a checkedlistbox in this manner, however I feel it suits my purposes perfectly in that I want there to be no doubt whatsoever as to where the text files will be read from and written to.
All the best.
I agree with the comments that radio buttons would be the usual UI element when only a single item is 'checked', but if you want to stick with a CheckedListBox for your UI, you can try something like this:
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
CheckedListBox.CheckedIndexCollection checkedIndices = checkedListBox1.CheckedIndices;
if (checkedIndices.Count > 0 && checkedIndices[0] != e.Index)
{
checkedListBox1.SetItemChecked(checkedIndices[0], false);
}
}
You also might want to set CheckOnClick to true for the CheckedListBox.
Edit
Updated the code per your comment to deselect an item if it is unchecked. The problem is that unchecking the previously checked item causes the event to fire again. I don't know whether there is a standard way to handle this, but in the code below, I detach the handler before calling SetItemCheck, then reattach the handler. It seems like a clean way to handle this, and it works. If I find that there is a recommended way to handle this, I will update my answer.
HTH
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
CheckedListBox.CheckedIndexCollection checkedIndices = checkedListBox1.CheckedIndices;
if (checkedIndices.Count > 0)
{
if (checkedIndices[0] != e.Index)
{
// the checked item is not the one being clicked, so we need to uncheck it.
// this will cause the ItemCheck event to fire again, so we detach the handler,
// uncheck it, and reattach the handler
checkedListBox1.ItemCheck -= checkedListBox1_ItemCheck;
checkedListBox1.SetItemChecked(checkedIndices[0], false);
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}
else
{
// the user is unchecking the currently checked item, so deselect it
checkedListBox1.SetSelected(e.Index, false);
}
}
}
I have a simple form with list as a data source, a binding source bound to the list, and a combo box bound to the binding source with some fields editing whatever the bindingSource.Current is. What I would like to do is if a bool is set pop up a dialog asking if they want to save changes before they change items. If they say no I want to call CancelEdit() if yes I want to keep them on the current item so they can click the save button. How do I do this?
The second part of my question is because my underlining data source is a List<View> will CancelEdit() even do anything? (View is just a class with string Name and a List<CustomColumn>
Edit:
Let me elaborate on what I am doing to maybe help explain what I am doing.
I have a list of View, these elsewhere in the program will be enumerated to generate a DataGridView. What this menu is for is adding new "Views" and changing the order of the columns in the view (it never actually edits the the CustomColumn just adding items and changing the order of the list<CustomColumn>). What I want to happen is if someone presses cancel or changes to a new view by using the combo box without saving it will undo any changes they made to the List<CustomColumn>
If I infer your question correctly, then the answer is not one that you're going to like; the ComboBox has no mechanism for cancelling a change of selection. I wish it did, as I have come across this issue time and time again. This is how i'd work around the limitation:
bool ignoreEvent = false;
object lastSelectedItem = null;
void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
if (ignoreEvent) return;
if (CheckForChanges()) {
if (MessageBox.Show("Do you want to save changes?", "Save changes", MessageBoxButtons.YesNo) == DialogResult.Yes) {
ignoreEvent = true;
comboBox1.SelectedItem = lastSelectedItem;
ignoreEvent = false;
}
else {
// call CancelEdit() here
}
}
lastSelectedItem = comboBox1.SelectedItem;
}
Basically, the above code offers the means to revert the ComboBox to its previous selected value, without calling any event handler code in the process. Users will briefly see their item selection change, then snap back if they answer 'No' on the popup.
Also, you're correct in your assertion that CancelEdit() will essentially do nothing - the generic List collection does not support change detection. You may wish to use a DataTable or an ObservableCollection, both of which support change detection.