I need to check what treeviewitem the user selected everytime it changes. I used the SelectedItemChangedmethod or whatever this is called. It works fine but now I need to do a switch case for every possible treeviewitem. But I can't do that since I have no idea how to get the name of it. I checked on internet but some people said to use treeview.SelectedItem but it returns System.Windows.Controls.TreeViewItem Header: Items.Count:0. I was wondering if I could do that entirely in the .cs code file or if I had to use data binding and such.
Thanks for your help.
EDIT:
Here is how i setup the treeview and the treeviewitems. They are all like the second example.
<TreeView x:Name="treeview" Margin="10,10,0,4" HorizontalAlignment="Left" Width="192" Background="#FFA45353" SelectedItemChanged="treeview_SelectedItemChanged"">
<TreeViewItem IsExpanded="False">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/smallicons/icon.jpg"/>
<TextBlock Text=" Main" FontSize="14"/>
</StackPanel>
</TreeViewItem.Header>
<!--==============================================================================================-->
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="First" Foreground="Black" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<!--==============================================================================================-->
Are you looking for this?
TreeViewItem selectedNode = (TreeViewItem)treeView.SelectedItem;
string strSelectedNode = selectedNode.Header.ToString();
The above snippet assumes a few things though.
By the way, this code is part of
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem selectedNode = (TreeViewItem)treeView.SelectedItem;
MessageBox.Show(selectedNode.Header.ToString());
}
Ok...As per your treeview and treeview Item setup, you have to code something like this..
private void treeview_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem selectedNode = (TreeViewItem)treeview.SelectedItem;
var sp = selectedNode.Header as StackPanel;
var tb = sp.Children[0] as TextBlock;
var selecteditem = tb.Text;
//MessageBox.Show(selecteditem);
switch (selecteditem)
{
case "Main":
MessageBox.Show(selecteditem);
break;
case "First":
MessageBox.Show(selecteditem);
break;
default:
MessageBox.Show("no matching item found");
break;
}
}
Here, I am digging in the selected tree view item to get the Text of selected tree view item. You may have to tweak the code a bit to get it fully operational. Let me know if you need any help with it.
Related
My application uses TreeView populated with custom nodes defined in TreeView.ItemTemplate. Content of each node is wrapped into StackPanel with Node_ContexMenuOpening event that populates context menu based on some application properties, which is working.
XAML:
<TreeView x:Name="treeNodes" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="TreeNodes_ContextMenuOpening">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type c:MyCustomType}" ItemsSource="{Binding MyCustomTypeChildren}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="Node_ContextMenuOpening" >
<Image Source="Frontend\Images\import.png" MaxWidth="15" MaxHeight="15"/>
<TextBlock Width="5"/>
<TextBlock Text="{Binding CustomTypeName}" MinWidth="100"/>
<TextBlock Width="10"/>
<Image Source="CustomImagePath" MaxWidth="15" MaxHeight="15"/>
<TextBlock Width="5"/>
<TextBlock Text="{Binding CustomTypeName2}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code behind:
private void Node_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
// get context menu and clear all items (empty menu with single placeholder item
// is assigned in XAML to prevent "no object instance" exception)
ContextMenu menu = fe.ContextMenu;
menu.Items.Clear();
// populate menu there
}
I would like to have same functionality on TreeView (treeview specific context menu when right clicking on empty area of treeview), which also works.
private void TreeNodes_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
TreeView tw = sender as TreeView;
ContextMenu menu = tw.ContextMenu;
menu.Items.Clear();
// poopulate menu there
}
But the issue is that TreeNodes_ContextMenuOpening is fired even after right clicking at TreeView node, right after Node_ContextMenuOpening is handled, which overwrites context menu for clicked node. I tried to solve it using:
// also tried IsMouseOver and IsMouseCaptureWithin
if (tw.IsMouseDirectlyOver)
{
// handle TreeNodes_ContextMenuOpening event there
}
but without success. Any suggestions?
Thanks in advance.
You can try using the ContextMenuEventArgs.Handled value. https://learn.microsoft.com/en-us/dotnet/api/system.windows.routedeventargs.handled?view=netcore-3.1#System_Windows_RoutedEventArgs_Handled
Gets or sets a value that indicates the present state of the event handling for a routed event as it travels the route.
Example
protected override void OnPreviewMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true; //suppress the click event and other leftmousebuttondown responders
MyEditContainer ec = (MyEditContainer)e.Source;
if (ec.EditState)
{ ec.EditState = false; }
else
{ ec.EditState = true; }
base.OnPreviewMouseRightButtonDown(e);
}
I have a combobox which gets its Items from some scan function.
If the user select an element, in the next time, the user's chosen item should be selected (if it is present on the scan function output). The problem is that I cannot select it.
Here is the declaration of the ComboBox:
<ComboBox Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" Text="{Binding Name}" IsEditable="False"/>
and here what I have tried so far:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string portNameSetting = Settings.Default["SerialPortName"].ToString();
SerialPortNames.ItemsSource = SerialPort.GetPortNames();
foreach (string SerialPortNameItem in SerialPortNames.Items)
{
if (SerialPortNameItem == portNameSetting)
{
SerialPortNames.Text = SerialPortNameItem; // why this is not working
break;
}
}
}
by debugging this, I get the item selected in the combobox, but it seems that something override it and it is empty!
In your code you Binded the Text propery and also setting it from code behind
Remove Text="{Binding Name}" from the combobox
<ComboBox Width="200" Height="200" Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" IsEditable="False"/>
I'm probably not even asking this correctly, I am new to c#, but trying to help my 14 year-old son learn. I've created a listbox with items created with an ObservableCollection. Here is the XAML:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="105,205,886,63"
IsTabStop="True" SelectionChanged="PrintText"
ScrollViewer.VerticalScrollBarVisibility="Hidden" TabIndex="5" FontSize="36"
Background="Transparent" Foreground="#FF55B64C" FontFamily="Arabic Typesetting"
FontWeight="Bold" IsDoubleTapEnabled="False" SelectionMode="Single" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="blockNameList" Text="{Binding name}"/>
<TextBlock Text=" #"/>
<TextBlock Name="blockIdList" Text="{Binding id}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is how I created the ListBox Items:
var client = new HttpClient();
var uri = new Uri("http://theurlImusing");
Stream respStream2 = await client.GetStreamAsync(uri);
// DataContractJsonSerializer ser2 = new DataContractJsonSerializer(typeof(RootObject));
// RootObject feed2 = (RootObject)ser2.ReadObject(respStream2);
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<RootObject>));
ObservableCollection<RootObject> feed2 = ser.ReadObject(respStream2) as ObservableCollection<RootObject>;
var cardList = new List<RootObject>();
foreach (RootObject returnfeed in feed2)
{
string cid = returnfeed.id;
string cardname = returnfeed.name;
listBox1.Items.Add(new RootObject { id=cid, name=cardname });
}
I thought I would just use the SelectionChanged="PrintText" property of the listbox so that when I clicked on a listbox item, it would just change a textblock's text value. Ultimately, that is all I am trying to do...set a textblock or textbox to be equal to the "id" value that is clicked on in the ListBox.
void PrintText(object sender, SelectionChangedEventArgs args)
{
//What do I put in here??
}
Thanks very much for any insight! I need it!!
This is something that is much easier to do using data binding. You can bind the TextBlock.Text property directly to the ListBox using an ElementName binding:
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedItem.id}" />
Alternatively, if you set set SelectedValuePath="id" on the ListBox, then binding to SelectedValue will give you the "id" property:
<ListBox x:Name="listBox1" SelectedValuePath="id" ... />
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedValue}" />
As a side note (as #Rachel already noted in comments): you may as well just set the ItemsSource, rather than looping through and adding each manually. All you need is this:
listBox1.ItemsSource = feed2;
Edit
Ok, if you wanted to use the procedural approach, here's how you would do it. (No one would recommend this approach, especially if you're learning/teaching. Try to make full use of data binding, and view-viewmodel separation.)
void PrintText(object sender, SelectionChangedEventArgs args)
{
var listBox = (ListBox)sender;
RootObject selectedItem = listBox.SelectedItem;
someTextBox.Text = selectedItem.id;
}
If all you want to do is click an item in the ListBox and get it to show up in the TextBox, you don't need fancy binding (in that other answer) to do it. You can simply add a MouseUp event in the ListBox XAML:
MouseUp="ListBox1_MouseUp"
This would work similar to the SelectionChanged event you wanted to use.
You then right-click that function name in the XAML page and select "Go to definition". It will create the next function for you:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
}
Simply add in there to update the TextBox you want with the SelectedItem values from sender:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
ListBox lstBox = (ListBox)sender;
ListBoxItem item = lstBox.SelectedItem;
if (item != null) // avoids exception when an empty line is clicked
{
someBox.Text = item.name;
someOtherBox.Text = item.id;
}
}
I later found that blockNameList and blockIdList are not accessible via intellisense because they are within the DataTemplate of the ListBox, so I put someBox and someOtherBox, as references to other TextBoxes you would have to add to the XAML, outside of the ListBox. You would not re-write data inside the ListBox on the same item by clicking it. Even if you could reach the template's TextBlock to do it, you'd just be re-writing that same item with its own values, since it would be the SelectedItem!
Even though there are those that don't recommend this approach because they like binding everything - and in some cases you want binding to occur so that controls on the page update as a result of dependencies (i.e. do one thing to cause another), I find that manual methods of clicking a button/item/control to update something are just fine and avoid all the model/MVVM BS that has taken over WPF and over-complicated it.
Since there is no link button in WPF I created a link button using hyperlink and text block controls.
There are 3 controls:
<TextBlock Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" >
<Hyperlink Name="hyplnkIsActiveMarkets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveMarkets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActiveBudgets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveBudgets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActivePrograms" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActivePrograms" Text="Active" />
</Hyperlink>
</TextBlock>
All the link buttons calls same click method
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = new TextBlock();
if (objHyperlink == hyplnkIsActiveMarkets)
{
objTextBlock = txtblkIsActiveMarkets;
}
else if (objHyperlink == hyplnkIsActiveBudgets)
{
objTextBlock = txtblkIsActiveBudgets;
}
else if (objHyperlink == hyplnkIsActivePrograms)
{
objTextBlock = txtblkIsActivePrograms;
}
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
In the click method I check for the text block inside the hyper link individually using if condition.
Is there any easier way to do this? That's basically finding control inside a control?
UPDATE: you can not use VisualTreeHelper.GetParent(...) to get the parent of your hyperlink as you have mentioned hyperlink is not visual. corrected the answer.
See code below.
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink)[0];
// This will give logical tree first child of objHyperlink
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
See this article about logical tree on MSDN
I think you're going into the wrong direction by relaying your execution logic on controls and not on data.
You can, for example, bind a ICommand or RelayCommand to the buttons, or just subscribe different events, or define a custom DataTemplate where on mouse down the clicked control is assignable to some ModelView property.
Doing in way you do, you create tough coupling between UI and your execution logic.
In this case easier use WindowsForm then WPF.
I got it finally . Thanks to Maheep fa his help
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink).Cast<System.Windows.Documents.InlineUIContainer>().FirstOrDefault().Child;
I have the following XAML code as an example in my WPF application
<StackPanel Height="23" Name="MSpanel" Orientation="Horizontal" Width="138" Margin="37,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" >
<TextBox Height="23" Name="MTBox" Width="120" Text="0" />
<ScrollBar Height="23" Name="MSBar" Width="18" TouchUp="SBar_TouchUp" />
</StackPanel>
<StackPanel Height="23" Name="CSPanel" Orientation="Horizontal" Width="138" Margin="37,41,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBox Height="23" Name="CTBox" Width="120" Text="0" />
<ScrollBar Height="23" Name="CSBar" Width="18" TouchUp="SBar_TouchUp" />
</StackPanel>
I have this function:
private void SBar_TouchUp(object sender, TouchEventArgs e)
{
//what goes here?
//siblings.getFirst('textbox').text += 1;
}
What I was hoping to do, is have 1 function that controls these "Psudo" numeric up downs in WPF. If there was some way to have a unified function that could, reference the sibling textbox, so I only have to write it once. That would be ideal.
I'm very familiar with jQuery, and XAML looks like an HTML DOM, ... Is there a way to browse the tree?
I realize there are existing Numeric Up Downs available to download. This idea I believe would be good to know for the future in other endeavors as well. Thanks.
The solution that worked!
private void SBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (e.NewValue == 0) return; //abort here, no change
ScrollBar sb = (ScrollBar)sender;
StackPanel sp = (StackPanel)sb.Parent;
TextBox tb = (TextBox)sp.Children[0];
int change = e.NewValue < 0 ? 1 : -1;
sb.Value = 0; //this will invoke this function again
tb.Text = (Convert.ToInt32(tb.Text) + change).ToString();
}
Each element in the visual tree has a Parent and VisualParent property - as all elements are based on UIElement - either should give you the parent object.
In this case the parent of the ScrollBar is the StackPanel. You can then use the Children property of the StackPanel to get the collection of child objects. You know which is the ScrollBar (it's the sender) so the other must be the TextBox.
You can do something like this:
private void SBar_TouchUp(object sender, TouchEventArgs e)
{
//siblings.getFirst('textbox').text += 1;
var siblings = ((sender as FrameworkElement).Parent as Panel).Children;
var textbox = siblings.OfType<TextBox>().First();
textbox.Text = (int.Parse(textbox.Text) + 1).ToString();
}
but I would suspect that there are probably better ways to do what you want, like data binding or naming elements in attached properties.
Yeah, there are many properties for both Logical and Visual trees.
Like FrameworkElement.Parent or Panel.Children.
I don't think there is directly method to get sibling, but its not that hard to get index in list of children of parent and getting next item.