Access content in XAML from code behind without name - c#

I'm new to C# and XAML, so please excuse any obvious mistakes.
Let's say I wanted to create 100 checkboxes, all with the same layout, and when you click the checkbox, it makes the text in a label that's a child to that checkbox turn bold. I don't want to write out 100 functions for 100 checkboxes, I want to make a single function each checkbox can call that'll do the same thing.
<CheckBox VerticalContentAlignment="Center" Checked="CheckBox_Checked">
<WrapPanel>
<Image> Width="50" Source="Images/example.jpg"/>
<Label VerticalContentAlignment="Center">Extra cheese</Label>
</WrapPanel>
</CheckBox>
I'm able to get the WrapPanel nested under the CheckBox, but I can't seem to do the same to get the Label which is nested in the WrapPanel.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox _cb = (CheckBox)sender;
WrapPanel _wp = (WrapPanel)(_cb).Content;
Label _lb = (Label)(_wp).Content;
_lb.FontWeight = FontWeights.Bold;
}

The wrap panel class doesn't have a content attribute. What you can use however is the Children attribute in a combination with FirstOrDefault OfType. the method could look somethind like this.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox _cb = (CheckBox)sender;
WrapPanel _wp = (WrapPanel)(_cb).Content;
Label lbl = _wp.Children.OfType<Label>().FirstOrDefault();
lbl.FontWeight = FontWeights.Bold;
}

You can easily access the element you want without the need for hierarchical transformations that exist in HTML and there is no need for different transformations, so we will have the following code:
File.xml
<CheckBox VerticalContentAlignment="Center" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked">
<WrapPanel>
<Image Width="50" Source="Images/config.gif"/>
<Label VerticalContentAlignment="Center">Extra cheese</Label>
</WrapPanel>
</CheckBox>
File.cs
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (sender is CheckBox _cb)
{
if (_cb.FontWeight == FontWeights.Normal)
_cb.FontWeight = FontWeights.Bold;
else
_cb.FontWeight = FontWeights.Normal;
}
}

Related

how to access label name in nested listview datatemplate on a check box event

I have defined a label with name and I'm trying to access it but no luck. Let me explain my problem with my code.
<ListView Name="gridListView" ItemsSource="{Binding... }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Vertical">
<Label x:Name="vLabel" Content="{Binding VCValue}"/>
<ListView Name="checkBoxListView" ItemsSource="{Binding CList}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Margin="5" Click="CheckBox_Click" IsChecked="{Binding SelectedValue, Mode=TwoWay}" Content="{Binding Current, Mode=OneWay }"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the above code, I have two listview, gridListView and checkBoxListView. Here, I want to access the label vLabel which is inside the datatemplate of gridListView when the one of the value in checkbox(which is inside checkBoxListView) is clicked.
I understand it can't be accessed directly as its within datatemplate so i tried below code as suggested in other forums but gridListView.SelectedIndex is always -1 so i know i'm not doing the right thing. When I just hardcoded gridListView.SelectedIndex to index 0, 1 or 2 its giving me the right value of vLabel so the below code will work if gridListView.SelectedIndex is correct.
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox chk =(CheckBox)sender;
int index = gridListView.Items.IndexOf(chk.DataContext);
ListViewItem item = gridListView.ItemContainerGenerator.ContainerFromIndex(gridListView.SelectedIndex) as ListViewItem;
if (item!=null)
{
//get the item's template parent
ContentPresenter templateParent = GetFrameworkElementByName<ContentPresenter>(item);
DataTemplate dataTemplate = gridListView.ItemTemplate;
if (dataTemplate != null && templateParent != null)
{
var lab = dataTemplate.FindName("vLabel", templateParent) as Label;
var v = lab.Content;
}
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
//I can post this function if need be
....
}
Appreciate any help that will help me access vLabel.
Thanks in advance
you are setting focusable to false, so you cant select the item by clicking. also the checkbox-checked happens before the selection, even if you were to set focusable to true, so selected index would still be -1.
you can find your label simply like this:
public Label FindLabel(CheckBox checkBox)
{
var listView = VisualTreeHelper.GetParent(checkBox);
while (listView.GetType() != typeof(ListView))
{
listView = VisualTreeHelper.GetParent(listView);
}
return (listView as FrameworkElement).FindName("vLabel") as Label;
}
but i suggest you tell us what you want to achieve, because this doesnt seem like a clean solution.
can you perhaps do this on your StackPanel:
<StackPanel CheckBox.Checked="CheckBox_Click" Orientation="Vertical">
and then access your two desired properties as following:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var outerItem = (sender as FrameworkElement).DataContext;
var innerItem = (e.OriginalSource as FrameworkElement).DataContext;
}
and then do whatever you want to do?
Thank you for all your guidance. I did get hint from Milan's code to achieve solution for my issue. Here, I'm trying to get reference parent of listviewItem(i.e stackpanel) and then access its child. In my case, stack panels child at index 0 is Label and at index 1 is ListView. So then I go through visualtreehelper to get reference to its child at index 0 which is what i need access to. So here is the code snippet.
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
//to access parent of the checkbox
ListViewItem listViewItem =
GetVisualAncestor<ListViewItem>((DependencyObject)sender);
//to access parent of the listViewItem(which is parent of checkbox)
StackPanel stackPanel =
GetVisualAncestor<StackPanel>((DependencyObject)listViewItem);
int childCount = VisualTreeHelper.GetChildrenCount(stackPanel);
//to access child of stackpanel to access the current vLabel
var vValue = VisualTreeHelper.GetChild(stackPanel, 0) as Label;
}
private static T GetVisualAncestor<T>(DependencyObject o)where T : DependecyObject
{
...
}

How do I handle a selected item of LongListMultiSelector?

I have list of items in LongListMultiSelector - how to handle a selected item?
My LongListMultiSelector xaml:
<tkit:LongListMultiSelector Name="longlist" SelectionChanged="longlist_SelectionChanged">
<tkit:LongListMultiSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" FontSize="32" Tap="TextBlock_Tap"/>
</DataTemplate>
</tkit:LongListMultiSelector.ItemTemplate>
</tkit:LongListMultiSelector>
TextBlock tap event handler code:
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var itemTapped = (sender as FrameworkElement).DataContext as Book;
}
LongListMultiSelector SelectionChanged event handler code:
private void longlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
I found part of solution here, however, The problem if at least one item is selected, then textblockTap event doesn't handle - longlist_SelectionChanged event handles everything. How can i fix that?
Once you are using LongListMultiSelector, the SelectionChanged event is fired when item is added or removed. If you want to perform the action regardless item is added/removed, I've managed to do it like this (for a simle string):
private void longlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selectedItem = String.Empty;
if (e.AddedItems.Count > 0) selectedItem = e.AddedItems[0] as string;
else selectedItem = e.RemovedItems[0] as string;
MessageBox.Show(selectedItem); // do your work
}
It should run while items are selected separately by tapping, but this method will have problems when more items are added/removed at the same time - if you need it, then you should handle this also.
Your XAML DataTemplate.
<DataTemplate x:Key="listItemTemplate">
<StackPanel Orientation="Horizontal" Margin="4,4">
<TextBlock Tap="textblockTap" Margin="0,-7,0,0" Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}"/>
</StackPanel>
</DataTemplate>
In your CS page;
private void textblockTap(object sender, EventArgs e)
{
var file = (TextBlock)sender;
var ContentFile = (string)file.Text;
MessageBox.Show(ContentFile);
}
This example will show you the text of the selected item in the MessageBox.

c# How to get Listbox selection from Observable Collection

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.

Assigning event GotFocus to all TextBoxes in WPF-Aplication

I wanted to add GotFocus-Event to every Textbox in my WPF-Aplication, because its for Touch Devices and everytime a TextBox is in use the OSK should open. I got Problems with procedure adding the Event to my TextBoxes. The Aplication is already build for pc (I'm in an interhsip and my goal is it to bring this Apl. to Windows 8 Touch Devices).This is the link, where I got my Inspiration from: Add/Remove handler to textbox
and here is my Solution:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (Control tb in this.Controls)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.GotFocus += TextBox_GotFocus;
}
}
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
KeyBoardManager.LaunchOnScreenKeyboard();
}
when I want to run this Code I have the following Error:
Error 1 'OSK_Test.MainWindow' does not contain a definition for
'Controls' and no extension method 'Controls' accepting a first
argument of type 'OSK_Test.MainWindow' could be found (are you missing
a using directive or an assembly reference?)
What have I got to do, that it works? And of course it's the same with LostFocus!
You can do better, add below code to app.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(TextBox),
TextBox.GotKeyboardFocusEvent, new RoutedEventHandler(TextBox_GotFocus));
base.OnStartup(e);
}
The problem as I see it is that you are using an answer meant for a Winforms application with a Wpf application, in the Winforms your controls can be added to the main form, therefore this.Controls works, in Wpf your MainWindow has a Content Property which can contain only one item, usually a Grid, Canvas or Panel of some kind. This object is where your TextBox's are located.
Note: this will work only if your TextBox's are children of the MainWindows LayoutControl and not Embedded any deeper if it is embedded deeper you will need to name the Panel that they are contained in and iterate through that, also I am not suggesting that this is the proper way to approach your problem, just trying to point out what your problem is:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
foreach (Control tb in ((Panel)this.Content).Children)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.GotFocus += TextBox_GotFocus;
tb1.LostFocus += tb1_LostFocus;
}
}
}
Based on OP's comment:
You will need to give your StackPpanel a name or use an existing one if one exists. i.e.
<StackPanel x:Name="MyStackPanel" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100">
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox"/>
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox"/>
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox"/>
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox"/>
<TextBox Height="23" TextWrapping="Wrap" Text="TextBox"/>
</StackPanel>
Usuage would be something like this:
public MainWindow()
{
InitializeComponent();
foreach (Control tb in MyStackPanel.Children)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.GotFocus += TextBox_GotFocus;
tb1.LostFocus += tb1_LostFocus;
}
}
}
you can easily achieved it using user control with textbox inheritance .
public UserTextbox()
{
InitializeComponent();
this.GotFocus += (sender, args) =>
{
//your code here
};
}
This user control you can use every where in you project you want.
Add a GotFocus handler to your root element in your window.
Assuming your root element is a Grid, it would be
<Grid name="root" GotFocus="root_GotFocus">
In your code
private void root_GotFocus(object sender, RoutedEventArgs e)
{
TextBox tb = e.OriginalSource as TextBox;
if(tb != null)
{
//do your thing
KeyBoardManager.LaunchOnScreenKeyboard();
}
}

How to get GridViewItem from its child element?

Suppose I have a code in XAML like this:
<GridView>
<GridView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding test}" Click="ButtonClick" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Then how can I get which GridViewItem was selected? Because, normally what is done is to add the ItemClick functionality to the GridView itself, but in this case I am doing something customized and need to get the SelectedItem starting from the Button.
I tried code something like this:
void ButtonClick (object sender, RoutedEventArgs e)
{
var g = (GridViewItem)((Button)sender).Parent;
}
But it does not work (returns null). Please help!
Thanks!
Sure! Here's the code that I use when the ad control fails to load an ad (like when the machine is offline). In that case I remove it form the gridview. To do that I have to locate the ad's parent gridviewitem and remove the whole thing. I do it like this:
private void AdControl_ErrorOccurred_1(object sender, Microsoft.Advertising.WinRT.UI.AdErrorEventArgs e)
{
var _Item = sender as DependencyObject;
while (!(_Item is GridViewItem))
_Item = VisualTreeHelper.GetParent(_Item);
HubGrid.Items.Remove(_Item);
}

Categories