TreeView label editing question - c#

I have a treeview with nodes like this: "Foo (1234)", and want to allow the user to rename the nodes, but only the Foo part, without (1234). I first tried to change the node text in BeforeLabelEdit like this:
private void treeView1_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
e.Node.Text = "Foo";
}
But when I click the node to edit it, "Foo (1234)" appears in the textbox.
Okay, then let's try something else.
I set treeView1.LabelEdit to false, and then do the following:
private void treeView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (treeView1.SelectedNode == treeView1.GetNodeAt(e.Location))
{
treeView1.SelectedNode.Text = "Foo";
treeView1.LabelEdit = true;
treeView1.SelectedNode.BeginEdit();
}
}
}
And then in AfterLabelEdit, I set LabelEdit back to false.
And guess what? This doesn't work either. It changes the node text to "Foo" but the edit textbox does not appear.
Any ideas?
Thanks

Finally I have found a solution to this on CodeProject. Among the comments at the bottom, you will also find a portable solution.

Heh - I struck that one a few years back. I even left a suggestion on Connect (vote for it!) to allow the label to be changed in BeforeLabelEdit.
One option (in WinForms - it's a different story in WPF) is to use custom painting for your TreeNodes so that the actual label is still "Foo" and you custom draw the " (1234)" after it. It's a bit of a pain to get right though.

Related

WinrtXamlToolkit TreeView expand with single instead of double click

I am using the TreeView from the WinrtXamlToolkit. The default behavior of this control is to expand the nested items on double click of the header. The code responsible for this is here (TreeViewItem.cs line 1205).
private void OnHeaderMouseLeftButtonDown(object sender, PointerRoutedEventArgs e)
{
if (Interaction.AllowMouseLeftButtonDown(e))
{
// If the event hasn't already been handled and this item is
// focusable, then focus (and possibly expand if it was double
// clicked)
if (!e.Handled && IsEnabled)
{
if (Focus(FocusState.Programmatic))
{
e.Handled = true;
}
// Expand the item when double clicked
if (Interaction.ClickCount % 2 == 0)
{
bool opened = !IsExpanded;
UserInitiatedExpansion |= opened;
IsExpanded = opened;
e.Handled = true;
}
}
Interaction.OnMouseLeftButtonDownBase();
OnPointerPressed(e);
}
}
Is there a way to change this behavior to expand the items on single click or tap without actually copying the control and all it's related classes to my project?
It seems like an overkill to do this just to change a few lines of code.
I tried to do drag'n'drop stuff with that TreeView and was in a similar situation. My first move was to actually copy all the TreeView and its related classes and man there are a lot. There's a lot of internal stuff happening and I pretty much gave up interfering with it after a bunch of other stuff stopped working.
So my solution was to just have a specific control inside the ItemTemplate that handled dragging for me. For you this would be a Button whose Click you handle. In the eventhandler you will navigate up the visual tree to your TreeViewItem and change the IsExpanded.

WPF- Horizontal and Vertical position of a Run Element in a FlowDocument

Is there anyway to get the horizontal position(pixel) and vertical position(pixel) of a Run element in a FlowDocument?
Edit:
All i need to do is scroll to that position and make it the top line of the FlowDocument.
To Answer Your Question
The code needed to get the position of a content element in a document is all internal to .NET and not publically exposed. You would need access to an IContentHost implementation, which the built-in document viewers do not publically expose. So, there is no supported way to do what you are asking.
To Solve Your Actual Problem
There is a way to achieve your desired result of scrolling the element to the top of the view. What you want to do is scroll to the end of the document, then call BringIntoView on the element you want to have at the top.
There are multiple ways a FlowDocument can be displayed in an application. How you handle the scrolling depends on which control you are using to present the FlowDocument.
In a RichTextBox, use the ScrollToEnd method.
In a FlowDocumentScrollViewer, you will need to get its internal ScrollViewer and call ScrollToBottom on it. (You have to wait until the control is loaded before you can get a template part from it.)
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
mScrollViewer = mViewer.Template.FindName("PART_ContentHost", mViewer) as ScrollViewer;
}
In a FlowDocumentReader, the process is a bit more complex.
When the control is loaded, register for changes to the ViewingMode property and run the handler once to account for the starting value:
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
var descriptor = DependencyPropertyDescriptor.FromProperty(FlowDocumentReader.ViewingModeProperty, typeof(FlowDocumentReader));
descriptor.AddValueChanged(mReader, (s, a) => Reader_ViewModeChanged());
Reader_ViewModeChanged();
}
In the handler, dig in to find the ScrollViewer. It will only be present when the ViewingMode is set to Scroll:
private void Reader_ViewModeChanged()
{
mScrollViewer = null;
if (mReader.ViewingMode == FlowDocumentReaderViewingMode.Scroll)
{
var contentHost = mReader.Template.FindName("PART_ContentHost", mReader) as DependencyObject;
if (contentHost != null && VisualTreeHelper.GetChildrenCount(contentHost) > 0)
{
var documentScrollViewer = VisualTreeHelper.GetChild(contentHost, 0) as FlowDocumentScrollViewer;
if (documentScrollViewer != null)
{
documentScrollViewer.ApplyTemplate();
mScrollViewer = documentScrollViewer.Template.FindName("PART_ContentHost", documentScrollViewer) as ScrollViewer;
}
}
}
}
Once you have the ScrollViewer, you can call ScrollToBottom on it when desired.
Now, scroll to the bottom of the document, then call BringIntoView on your Run, and it should be at the top of the view.
Does not bring it to the top but just call BringIntoView on the Run. Save a reference to the Run.
It may be late but i still want to share the way i DID it in WPF.
You need an offset to do so.
As the above said: Flow gave you:
flow.ScrollToHome(); // Bottom
But also gave: ScrollToVerticalOffset (get from Rect)
if you have index (offset of the char/line) - you can find it in you saved data or get the TextPointer with flow.Selection.Start/End
TextPointer t_st = flow.Selection.Start;
double offset = flow.Document.ContentStart.GetOffsetToPosition(t_st);
private void gotoOffset(double offset)
{
TextPointer myTextPointer1 = flow.Document.ContentStart.GetPositionAtOffset((int)offset);
flow.Selection.Select(myTextPointer1, myTextPointer1);
flow.Focus();
Rect screenPos2 = myTextPointer1.GetCharacterRect(LogicalDirection.Forward);
double offset2 = screenPos2.Top;
Thread.Sleep(100);
flow.ScrollToVerticalOffset(offset2);
flow.Focus();
}
As the code above, We get the Rect from TextPointer, the Textpointer and get from Offset.
The focus just to make sure to place the cursor in right place.
Sometime the issue happen when you jump to many offset.
I recomment to trigger flow.ScrollToHome(); Before jump (because this ScrollToVerticalOffset true from the start, not any line)

C# ComboBox disable highlighting

I have custom ComboBox, where DropDownStyle = ComboBoxStyle.DropDown;.DropDown style is set because I want to set the Text property of the ComboBox to something outside the list of values. Everything works good, except that ComboBox is highlighting the text when it's left and when I click on the combobox editing is avaible. How can I cope with this?
To illustrate:
First Picture is where everything looks good, second is the highlight situation, third editing is on.
Try un-selecting the text after the DropDown closes:
void comboBox1_DropDownClosed(object sender, EventArgs e) {
this.BeginInvoke(new Action(() => { comboBox1.Select(0, 0); }));
}
If you are referring to disabling the highlighting and editing, then you might want to consider setting the DropdownStyle property to DropdownList.
yourComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
Tricky problem to solve. It seems to be from the Resize event. There are a lot of solutions that do something similar to this, but none that I've seen worked for me until I tried this. (This is a solution that does not require inheritance from ComboBox; inheriting is probably a much more straight forward solution, but requires you to always use your inherited class and never the actual ComboBox class.)
comboBox.Resize += (s, e) => {
if (!comboBox.IsHandleCreated)
return; // avoid possible exception
comboBox.BeginInvoke(new Action(() => comboBox.SelectionLength = 0));
};
Set the selection length to zero to get rid of the highlight, but when? Other examples do it in other places, but the problem seems to be specifically caused by Resize, so doing it after Resize fixes it consistently, at least for me. (Can still see it flicker when you resize the window though, but it always ends up ok.)
BeginInvoke ensures that it happens sufficiently after Resize to work, and the check for IsHandleCreated prevents it from being called before the handle is created, in which case BeginInvoke would throw an exception.
This slightly more complex version includes some checks to prevent a focused control from losing highlight, since it actually should have it. It also doesn't fire if the parent doesn't exist yet, or if the parent does not have an active control yet, both signs that things are too early.
comboBox.Resize += (s, e) => {
if (!comboBox.IsHandleCreated)
return;
comboBox.BeginInvoke(new Action(() => {
var parent = comboBox.FindForm();
if (parent == null)
return;
if (parent.ActiveControl == null)
return;
if (parent.ActiveControl == comboBox)
return;
comboBox.SelectionLength = 0;
}));
};
I tried to make a version that would 'preserve' the selection length rather than always set it to zero, but I couldn't get it to synchronize properly. Many Resize events can fire before the BeginInvoke delegates start to fire, so the preserved value will always be overwritten by the broken one. I tried saving them all in a Queue or Stack, but in both cases, I was unable to reverse the ordering (not really sure why, since that makes no sense).
To solve the same I have tried almost EVERYTHING:
setting the DropdownStyle property to DropdownList
this.BeginInvoke(new Action(() => { comboBox1.Select(0, 0); }));
combobox1.SelectionLength = 0;
changing comboBox.TabIndex
Not tried SendKeys.Send("{ESC}"); because it is not a reliable solution
Nothing helped.
The only stable and working solution was to move a focus on another Label control:
label.Focus();
You could also hide that label.
I know this post is old but recently I have the same problem with combobox.
Situation : I have an editable combobox which propose complete words when user write some letters.
But when I want to type a letter, combobox auto highlight the text and the next letter auto replace the previous.
Solution : I use a textbox to avoid any highlight like that:
<ComboBox IsTextSearchEnabled="False" IsEditable="True" x:Name="CMB_ClientName"/>
<TextBox Text="{Binding ElementName=CMB_ClientName, Path=Text}" TextChanged="ComboBoxChange" x:Name="TXT_ClientName"/>
And I generate the textbox TextChanged event :
private void ComboBoxChange(object sender, TextChangedEventArgs e)
{
//Clear ComboBox items
CMB_ClientName.Items.Clear();
//Auto Open DropDownList
CMB_ClientName.IsDropDownOpen = true;
//Get data from database (use entity framework 6.x)
dbEntity.Client.Load();
//Attribute Data to variable
var clients = dbEntity.Client.Local;
foreach (Client client in clients)
{
//If data begin with the texbox text, the data is add to the combobox items list.
if (client.Nom.ToLower().StartsWith(TXT_NomClient.Text.ToLower()))
{
CMB_ClientName.Items.Add(client.Nom);
}
}
}
I know this solution isn't realy beautifull, but it is for me the easiest solution to avoid highlight text and all the solutions in this post don't work for me.
I hope this solution will be helpfull, thanks for reading.
Math.
Ps: My apologies, my English is not very good. I hope you will understand me correctly.
Nothing worked for me ( I want the form to load with no highlighting in any combobox) until I set the combobox property TabStop to false. This meant that one of my buttons took the tab highlight which I didn't like so I set them all to false for start up and adjusted them programatically as needed.
I know this is an old thread, but my solution is similar to that of the others, but relies on the Form.ResizeEnd event. In its event handler, I iterate through the ComboBoxes and set ComboBox.SelectionLength to 0.
private void Form_ResizeEnd(object sender, EventArgs e)
{
foreach(ComboBox comboBox in parentControl.Controls.OfType<ComboBox>
{
comboBox.SelectionLength = 0;
}
}
This is what worked for me:
Set DrawMode to OwnerDrawFixed
Set cbxSubsystems.DrawItem event to the function below
private void cbxSubsystems_DrawItem(object sender, DrawItemEventArgs e)
{
Color BgClr;
Color TxClr;
if( (e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit )
{
// Do not highlight main display
BgClr = cbxSubsystems.BackColor;
TxClr = cbxSubsystems.ForeColor;
}
else
{
BgClr = e.BackColor;
TxClr = e.ForeColor;
}
e.Graphics.FillRectangle(new SolidBrush(BgClr), e.Bounds);
TextRenderer.DrawText(e.Graphics, cbxSubsystems.Items[e.Index].ToString(), e.Font, e.Bounds,
TxClr, BgClr, TextFormatFlags.Left | TextFormatFlags.VerticalCenter );
}

Programatically change display of the sort icon in datagrid column header

I asked this question previously and did not get an answer but now I have more detail.
Basically I want to programatically display the column sort icon in a wpf datagrid column.
I have the following code to do this:
private void dtgMain_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
dtgMain.Columns[0].SortDirection = ListSortDirection.Ascending;
}
This seems to set the sort order of the column but when the grid is drawn the icon does not show.
When I add a message box into the method it works fine. My question is twofold. Why would the message box cause the method to work? And how can I get it to work without the use of a messagebox?
This is the method working with the messagebox in it:
private void dtgMain_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("Here");
dtgMain.Columns[0].SortDirection = ListSortDirection.Ascending;
}
edit
Here is the method that is setting the datacontext of the datagrid
public void processLoad(string response)
{
XmlDataProvider provider = new XmlDataProvider();
if (provider != null)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(response);
provider.Document = doc;
provider.XPath = "/moo/response/data/load/panel";
dtgMain.DataContext = provider;
}
}
Please let me know if you need anymore information.
OK, I suspect what is happening is that the data layout changes caused by the DataContext update are being completed after your call to set the direction arrow, and it is therefore being erased after you set it. Interestingly, in my case it failed to work even when I put the messagebox in, perhaps because that was hanging up the UI thread while it displayed.
Could you try replacing the line that sets the sort direction with a similar call put on the dispatcher queue:
dtgMain.Dispatcher.BeginInvoke(new Action(() =>
{
dtgMain.Columns[0].SortDirection = ListSortDirection.Ascending;
}), DispatcherPriority.ApplicationIdle);
and see if that works?

WinForms TreeView - how to manually "highlight" node (like it was clicked)

I would need to know how to let the programatically selected node make graphically in the state "selected" like the user clicked on it. SelectedNode only makes this one internally selected. Thank you very much!
The reason it does not show as highlighted is due to the tree view not having focus. This is in a button click event on my test form:
TreeView1.SelectedNode = TreeView1.Nodes(2);
TreeView1.Focus();
Which highlights the node properly. if you remove the Focus(); call it doesn't highlight until you click into the tree view (anywhere in the tree view, not necessarily on to the node that you want to be selected).
TreeView1.SelectedNode.BackColor = SystemColors.HighlightText; // This will work
Above solutions will only set the focus on it but will not change the highlight view of it.
This works for me for .net 3.5:
Set the treeview component's DrawMode property to: OwnerDrawAll
Then in the DrawNode event write the following:
if (((e.State & TreeNodeStates.Selected) != 0) && (!MyTreeView.Focused))
e.Node.ForeColor = Color.Blue;
else
e.DrawDefault = true;
And in the BeforeSelect event have:
if (MyTreeView.SelectedNode != null)
MyTreeView.SelectedNode.ForeColor = Color.Black;
e.Node.ForeColor = Color.Blue;
I don't know if it helps you or not but check the taborder of the the page and make sure that the tree view control has tab order of 0
Here is what I got to work:
void myProcedure()
{
// Hookup a DrawMode Event Handler
this.myTV.DrawNode += myTV_DrawNode;
// Set DrawMode and HideSelection
this.myTV.DrawMode = TreeViewDrawMode.OwnerDrawText;
this.myTV.HideSelection = false;
// Make sure the TreeView has Focus
this.myTV.Focus();
// Make sure the TreeView is Selected
this.myTV.Select();
// If the TreeView has a Node, I want to select the first Node to demonstrate.
if (this.myTV.Nodes.Count > 0)
{
// Make sure the node is visible
this.myTV.Nodes[0].EnsureVisible();
// Make sure the Node is Selected
this.myTV.SelectedNode = myTV.Nodes[0];
}
// Make sure the SelectedNode IS the Node that we programmatically want to select.
textBox1.Text = this.myTV.SelectedNode.Text;
// if we display sanityCheck1 string, it actually is the correct node.text
// Make sure .NET runtime knows the Node is selected
textBox1.Text += " is Selected = " + this.myTV.SelectedNode.IsSelected.ToString();
}
Following up: laalto answered the How to HighLight the TreeView.Node. The following code in the DrawNode Event Handler, from samball's answer, properly highlights the TreeView.Node based on its Selected State.
private void myTV_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
// first, let .NET draw the Node with its defaults
e.DrawDefault = true;
// Now update the highlighting or not
if (e.State == TreeNodeStates.Selected)
{
e.Node.BackColor = SystemColors.Highlight;
e.Node.ForeColor = SystemColors.HighlightText;
}
else
{
e.Node.BackColor = ((TreeView)sender).BackColor;
e.Node.ForeColor = ((TreeView)sender).ForeColor;
}
}
Platform = C# .NET 4.5 in Windows 10, Visual Studio 2015
TreeView1.SelectedNode = TreeView1.Nodes(2);
this.ActiveControl = TreeView1;
This works for me (.net 4.7)
The underlying Win32 control supports this (think it's TVIS_DROPHILITED), but I can't see the same functionality exposed through the TreeView control.
As theraneman says, you could fake it with the TreeNode.ForeColor and BackColor properties...
I had an similar issue and wanted to have a TreeView node selected (highlighted) on form load.
Maybe someone has the same problem, too.
I first tried Pondidum's solution. Without success.
But then I found the solution in another thread: Simply set the TabIndex of the TreeView to 0.
In that case you don't need to set the focus. Just choose the node that should be selected by using SelectedNode and set the TabIndex. That's it.
Not sure, but can you not change the background color of that node?

Categories

Resources