Add adorner to bound children? - c#

<ItemsControl Name="CanvasTableMap" ItemsSource="{Binding}" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" ItemTemplate="{DynamicResource DataTemplate1}">
<ItemsControl.Resources>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel Background="{DynamicResource ContentBackground}" />
</ItemsPanelTemplate>
<DataTemplate x:Key="DataTemplate1">
<Button Canvas.Left="100" Content="{Binding Name}" Template="{DynamicResource ButtonTableTemplate}"></Button>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Here is my code.No problem with that. I have created an adorner and i would like to add an adorner for each button when i want. It is a little difficult as i dont know how to get the Buttons. CanvasTableMap.Items returns the Model so i dont know how to get access to the controls efficiently.

An easy way to do that is to define a handler for the Loaded event of the button, and add the adorner in that handler:
XAML
<Button Canvas.Left="100" Content="{Binding Name}" ... Loaded="Button_Loaded" />
Code-behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var layer = AdornerLayer.GetAdornerLayer(button);
// Add the adorner
...
}
If you don't want to use code-behind, you can create an attached behavior (either with System.Windows.Interactivity or by creating an attached property)

You can use the ItemContainerGenerator to get the control created from the data (ContainerFromItem). Usually doing things that way is not such a good idea though.

Related

C# WPF DataBinding

Im new in wpf programming and i have an application with MVVM. I have a StackPanel and inside the StackPanel there is a ItemsControl named ListView and the Itemsource is my ObserveCollection NewProducts which conatins ProductID and Name.
So what is my code does, well it generates buttons with Names from my Collection NewProducts and i wanna give this buttons Command that triggers when i click on them.
I try this:
<StackPanel Grid.Row="1">
<ItemsControl x:Name="ListViewProducts" ItemsSource="{Binding NewProducts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" Width="auto" Height="auto">
<StackPanel>
<Border Width="100" Height="40" CornerRadius="5">
<Button Content="{Binding Name}" Tag="{Binding ProductId}" FontWeight="Bold" Command="{BindingSaleViewModel.SendCommand}"/>
</Border>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
But when i run my program and when i click on my buttons nothing happens. Its because the Command that i have in my SaleViewModel was not found in Product which is my Model class. And i wanna know if i can somehow get the path to my Command in my ViewModel.
Thanks, and please forgive me mine English, I know its bad.
Binding methods in WPF is unfortunately not that simple.
The command is actually a class, that implements the ICommand interface.
You can find an example here: How to Bind a Command in WPF.
Otherwise you can bind the click event to a method in code-behind <Button Click="Button_Click"/>
In code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Add your code here...
}
Give your root element a name. eg:
<UserControl x:Class="blah"
other stuff
x:Name="root">
Then you can reference the root element in your binding, who's datacontext will (presumably?) be your SaleViewModel:
<Button Content="{Binding Name}" Tag="{Binding ProductId}" FontWeight="Bold"
Command="{Binding DataContext.SendCommand, ElementName=root}"
CommandParameter="{Binding}"/>
Also note the CommandParameter binding which will pass the button's data context (Product) through to your command, otherwise you won't know which product the command relates to.

How to i add an Item Click event to an items control

I am having problems with generating a click event for items inside an items control. I am new to xaml nad wpf. Can you the experts help me out. Below is the code that i have come up with but now having no clue on how to add a click event to for the generated items. Will very much appreciate your responses. Thank you for reading
<ItemsControl ItemsSource="{Binding Text, Source={StaticResource TextContainer}}">
<!--text is an object bein made public from TextToDisplay. There can be many objects released ratger than one in this case-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ItemWidth="100"
ItemHeight="100" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<Border CornerRadius="100"
Background="BlueViolet">
<Button Margin="20"
Content="{Binding}"
HorizontalContentAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
probably the easiest thing you can do is create your own user control... make it a custom button and add click event to that control so everytime the listbox add your control it will already have a click_event built in.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// add your code here.
}
then for the listbox do something like that...
private void AddItemsButton_Click(object sender, RoutedEventArgs e)
{
ListBoxTest.Items.Add(new UserControl1());
}
this adds a usercontrol button to the listbox everytime you click on a button titled "add items"

ListView SelectItem Binding not working as expected

I have a fairly straight forward listview control for my Windows 8 XAML/C# application. I am binding the listview to a PersonList and that works correctly. However, what I'd like to do and haven't been able to find the answer for is I want the to click an item in the listview and be able to display the PersonSingle object to the other textboxes on the screen.
I've read some posts that indicate that the listview may not be the right control for this operation. Am I missing something in my listview that would allow me to do this operation or should I use a different control?
<ListView
x:Name="itemListView"
Visibility="Visible"
Width="auto"
Height="auto"
ItemsSource="{Binding PersonList, Mode=TwoWay}"
SelectedItem = "{Binding PersonSingle, Mode=OneWay}"
ItemTemplate="{StaticResource person80Template}"
SelectionMode="Single"
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Stretch">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="7,7,0,0">
<Button
AutomationProperties.Name="PersonValue"
Content="{Binding PersonName}"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
UPDATE:
So I figured out how to do it in a non-MVVM way (or at least not in a true MVVM way)
I added an ItemClick event to my ListView:
ItemClick="itemListView_ItemClick"
And then in the code behind I added the following:
private void itemListView_ItemClick(object sender, ItemClickEventArgs e)
{
VM.PersonSingle = ((Person)e.ClickedItem);
}
That works, but like I said, it doesn't feel very MVVM'ish. If you have any suggestions on how to make it work without having to manually set the PersonSingle object please answer below.
Your ItemClick solution is the right solution. You could create an attached behavior if you are opposed to handling events, but that's your choice.

How do I get access to the item in a ListBox that is being rendered rather than its source data?

In a continuation from my question here
I tried using the solution provided in the linked answer, however when I give it the image which the ListBox is binding to, it gives me the wrong position, because the Item Template is loading in a source URL rather than the actual image, and it seems the data is null.
My Item Template looks like this:
<ListBox ItemsSource="{Binding Items}"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
Height="64" Name="listBoxSource"
VerticalAlignment="Bottom"
SelectionChanged="listBoxSource_SelectionChanged" Canvas.Left="32" Canvas.Top="365" Width="596"
>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image x:Name="ListImage" Source="{Binding thumbnailURL}" Height="64" Width="64"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How do I get access to "ListImage" for the currently selected Item?
Every ItemsControl has an ItemsContainerGenerator, which (surprisingly enough) is responsible for generating a control for each item in the list. It provides a few useful methods for finding the container of a given item, and vice versa. You can use it like this:
private void listBoxSource_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = (ListBox) sender;
var containerGenerator = listBox.ItemContainerGenerator;
var container = (UIElement)containerGenerator.ContainerFromItem(listBox.SelectedItem);
}
You can then use the variable container with the solution from your other post to find the coordinates,
Point position = container.GetRelativePosition(Application.Current.RootVisual);
And on a side note, in your DataTemplate you don't need the StackPanel, since the ListBox is providing that with its ItemsPanelTemplate.
<DataTemplate>
<Image x:Name="ListImage" Source="{Binding thumbnailURL}" Height="64" Width="64"/>
</DataTemplate>
on tap event you can do this:
private void image_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
Image selectedImage = e.OriginalSource as Image;
}
is this what you need?
I would suggest a different solution because the way you are trying to solve it (finding the control) is prone to error.
Make the DataTemplate a resource and refer to it in the ListBox by using ItemTemplate="{StaticResource myTemplate}"
Next, when an item is selected, add a ContentControl to the Canvas, set its ContentTemplate to the same DataTemplate and the DataContext to the SelectedItem.
This way you only have to define the template once (one place to maintain it) and do not have to walk the VisualTree.

How to Set focus on the 1st Item of Nested Listbox in Silverlight?

I have a List box which contains another lisbox Inside it.
<ListBox x:Name="listBoxParent">
<ListBox.ItemTemplate>
<DataTemplate>
<Image x:Name="thumbNailImagePath" Source="/this.jpg" Style="{StaticResource ThumbNailPreview}" />
<TextBlock Margin="5" Text="{Binding Smthing}" Style="{StaticResource TitleBlock_Artist}" />
</StackPanel>
<StackPanel Style="{StaticResource someStyle}" >
<ListBox x:Name="listBoxChild" Loaded="listBoxChild_Loaded" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="5" Text="{Binding myText}" Width="300"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
</ListBox.ItemsPanel>
</ListBox>
Now while I try to focus to the 1st item of the child List Box It focus repeteadly(as it get repeated inside parent Listbox) on 1st all the 1st items of parentlistbox, wish I could have provide a Screen shot for Better Understanding. But Can't
public void listBoxChild_Loaded(object sender, RoutedEventArgs e)
{
var myListBox = (ListBox)sender;
myListBox .ItemsSource = PageVariables.eOUTData;//listboxSongsData;
myListBox .SelectedIndex = 0;
myListBox .Focus();
}
Thanks,
Subhen
It's difficult to determine what you're trying to accomplish in your code.
One idea:
Have you considered changing the inner ListBox to an ItemsControl? If you needed it to scroll as well, you could layer it in a ScrollViewer.
Second idea (possibly an additional idea):
Every item that created in the item template is calling the Loaded event. Don't you want that to happen only once? Remove the event from the inner ListBox and set focus once the outer list has been loaded.

Categories