Image selection canvas - wpf

Acutally i'm doing a WPF application where user can select Image from a panel. There are two Button inside the panel (right arrow and left arrow). I'm not sure what is the name of the panel but i attach an image in my question. So, i want to ask how do i create the panel? Using a canvas and put the image inside the canvas? hm... and the buttons, I totally don't have any ideal how to do it.
p/s: i'm wpf newbie
Image:

Here is one way to do it, using the standard ListBox control:
First, we need a pair of Previous/Next buttons and a ListBox. We make the ListBox lay out its items horizontally by changing its ItemsPanel:
<DockPanel Width="200" >
<Button x:Name="_prev" Content="<<" />
<Button x:Name="_next" Content=">>" DockPanel.Dock="Right" />
<ListBox x:Name="_myList" Loaded="OnMyListLoaded" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<TextBlock Text="Image1 " />
<TextBlock Text="Image2 " />
<TextBlock Text="Image3 " />
<TextBlock Text="Image4 " />
<TextBlock Text="Image5 " />
<TextBlock Text="Image6 " />
<TextBlock Text="Image7 " />
</ListBox>
</DockPanel>
In the ListBox' Loaded event, we use VisualTreeHelper to search through its Template and find its built-in ScrollViewer. Once we find it, we hook it up to the two buttons we created:
private void OnMyListLoaded(object sender, RoutedEventArgs e)
{
var scroller = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(_myList, 0), 0) as ScrollViewer;
if (scroller != null)
{
_prev.Command = ScrollBar.LineLeftCommand;
_prev.CommandTarget = scroller;
_next.Command = ScrollBar.LineRightCommand;
_next.CommandTarget = scroller;
scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
}
}

This might help you:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Name="LeftB" Grid.Column="0" />
<Button Name="RightB" Grid.Column="2" />
<StackPanel Orientation="Horizontal" Name="Images" Grid.Column="1" />
</Grid>
The images can be the Children of the StackPanel.
Of course you should set the size of the the images as a function of their number and StackPanel's Width. The best way might be to handle Width change event of the StackPanel.

Related

WPF : ListBox.ItemTemplate doesn't react when click on text or image wpf xaml

I'm new to Xaml and WPF . I'm using the following code to extract botton title and image from RSS feeds .
The problem that the botton react only when user click on the border ... it dosen't react when user click on text or image .
<ListBox.ItemTemplate >
<DataTemplate >
<Button Background="{Binding C:AccentColors}" Width="400" Height="100" HorizontalAlignment="Left" >
<Grid Width="400" Height="100" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding XPath=enclosure/#url}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock TextWrapping="Wrap" Text="{Binding XPath=title}" FontWeight="Bold" Grid.Column="2"/>
</Grid>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You have to add the Click event in your button.
<Button Background="{Binding C:AccentColors}" Width="400" Height="100" HorizontalAlignment="Left" Click="MethodNameHere".....
and also you have to create and implement the method in the Window C# file
The common mistake is that somebody placed TextBlock on top of button without realizig it, because TextBlock has transparent Background.
Visually, the Text of TextBlock may be outside Button, but since TextBlock alignment is set to strech by default, it fills entire area.
hard to say if this is your case...
<Grid>
<Button Content="I'm not working" Margin="0,100,0,0" />
<TextBlock Text="I'm in top left corner" />
<Grid />
if you set TextBlock.Background to Red, you entire grid would be red.
The button is taking the clicks. You need a handler, either a command binding or if you're not using MVVM - a code behind method:
<Button Click="ButtonBase_OnClick"
Handler will look like this:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
}

How to align buttons horizontally and spread equally with xaml windows phone

I have an ObservableCollection which contains ViewModel which in turns defines my buttons definitions.
I've been at it for hours, reading articles after articles but to no avail. I've tried using a Listbox and this is the closest I've got to. My buttons are getting build horizontally but assuming I'm displaying 3 buttons, I want one displayed on the left, one displayed in the middle and one displayed on the right.
<ListBox Grid.Row="2" ItemsSource="{Binding Links}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<StackPanel Background="Beige" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Grid.Column="{Binding Column}"
Grid.Row="0"
Width="90"
Height="90">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36"
Margin="5" Stretch="UniformToFill"
HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}"
Foreground="#0F558E"
FontSize="18"
HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
As you can see, I set the column dynamically using a property from my ViewModel but I no longer have a grid as I couldn't get it to work, but ideally I'd like to use a grid so that I can specify in which Column to set the button.
When I use a StackPanel, it works but the buttons are right next to each other rather than being split evenly through the entire width of the screen.
I've done something similar to the above using ItemsControl and using a grid, and I can see each button getting added but they overlap each other in row 0, col 0. If I bind the row to the Column property for testing purposes, it build it correctly but my buttons are displayed on different rows which is not what I want. I want each button to be aligned horizontally.
How can I achieve this? I know I could just hard code 3 buttons and just change their icons and text and handle the relevant code by passing the relevant button as binded parameter, but ideally I'd like to build the buttons dynamically in a grid and position them using the column.
Note that the number of column would be fixed i.e. 3, as I'll never have more than this.
Thanks.
but ideally I'd like to use a grid so that I can specify in which
Column to set the button.
In any Xaml variant, why not just use that Grid, such as shown below, where the Grid is set to consumes all the horizontal space.
Then with the grid's center column to be star sized and to have the rest of the remaining space be consumed after button 1 and button 3, which auto size into their own spaces:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"/>
<Button Grid.Column="1" HorizontalAlignment="Center"/>
<Button Grid.Column="2"/>
</Grid>
If that fails, set button one's HorizontalAlignment to be Left and button three's as Right.
As an aside with the list box, it may not be stretching to the whole horizontal size of the screen. Check out my answer to a WP8 sizing issue:
ListBoxItem HorizontalContentAlignment.
I eventually found the answer to my problem in an article I found on the web.
You can check it out here: Using Grids with ItemsControl in XAML
In short, you need to subclass the itemsControl and you need overwrite the GetContainerForItemOverride method which will take care of copying the "data" of the ItemTemplate to the ContentPresenter. In this instance, the row and column, but for my requirement, it is just the Column, since my row will always be 0.
Here is core part of the code if you don't want to check the full article which resolve the problem of setting controls horizontally in a grid using ItemsControl but note the article takes care of creating rows & columns dynamically as well, which I'm not interested in for my project.
public class GridAwareItemsControl : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
ContentPresenter container = (ContentPresenter)base.GetContainerForItemOverride();
if (ItemTemplate == null)
{
return container;
}
FrameworkElement content = (FrameworkElement)ItemTemplate.LoadContent();
BindingExpression rowBinding = content.GetBindingExpression(Grid.RowProperty);
BindingExpression columnBinding = content.GetBindingExpression(Grid.ColumnProperty);
if (rowBinding != null)
{
container.SetBinding(Grid.RowProperty, rowBinding.ParentBinding);
}
if (columnBinding != null)
{
container.SetBinding(Grid.ColumnProperty, columnBinding.ParentBinding);
}
return container;
}
}
The final xaml looks like this:
<controls:GridAwareItemsControl ItemsSource="{Binding Links}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Background="Pink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Grid.Column="{Binding Column}"
Grid.Row="0"
Width="120" Height="120">
<ContentControl>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Image}" Width="36" Height="36" Margin="5"
Stretch="UniformToFill" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Description}" Foreground="#0F558E"
FontSize="18" HorizontalAlignment="Center"/>
</StackPanel>
</ContentControl>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</controls:GridAwareItemsControl>
Once I used the new control, my buttons were correctly placed inside the grid, and therefore were evenly spaced out as the grid took care of that wit the ColumnDefinitions.
If anyone knows how to achieve the same without having to create a new control and overriding the method (in other words, pure XAML), please post it as I'd be very interested to see how this can be done.
Thanks and thank you to Robert Garfoot for sharing this great code!
PS: Note that I've simplified my xaml in order to create a test sample without any style on the buttons, so these are rather large if you try based on this sample.
UPDATE:
Small typo correction but my grid column definition was defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
but as #OmegaMan suggested, to be evenly spread, it should have been defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
I was able to accomplish this with a stackpanel inside of a grid, avoiding columns altogether. If you set the stackepanel's HorizontalAlignment to "center", it will center itself inside the grid and grow as buttons are added, still staying centered inside of the grid. Then it's just a matter of margins to have the buttons equally spaced:
<Grid>
<StackPanel
VerticalAlignment="Stretch"
HorizontalAlignment="Center"
Orientation="Horizontal"
>
<Button HorizontalAlignment="Center" Content="Add" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Edit" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Remove" Width="104" Height="32" Margin="24,0"/>
</StackPanel></Grid>

Limit width of TextBox to the width of the ListView that contains it

I am working on a MVVM app (the examples here are not very MVVM though, its just a mockup).
I have a ListView, that binds to a collection of TextItemViewModel instances, the view for these are defined in TextItemView. Each TextItemView contains a Grid with one Auto column and on * column to fill. The fill column contains a TextBox where the user can enter text.
I want the TextItemViews to fill the horizontal space of the ListView (this works), like so:
However, when a user enters long text in a box, I want it to expand in Height but not in Width. At the moment, the Width increases and the ListView gets a scrollbar, like so:
My ListView code looks like this: (I've taken out the MVVM Binding code and set the ItemsSource in the code-behind for this example)
<ListView Name="listview"
SelectionMode="Single"
Margin="5"
MinHeight="50"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch" />
And my TextItemView code looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Content="SomeButton"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="5"
Grid.Column="0" />
<TextBox Text="{Binding Path=NoteText}"
TextWrapping="Wrap"
AcceptsReturn="True"
AcceptsTab="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
HorizontalAlignment="Stretch"
Margin="5"
Grid.Column="1" />
</Grid>
Can anyone advise me on how to force the TextItemView to fill the width of the ListView (as it does), but to then wrap the TextBox and expand TextItemView in Height when the TextBox fills up?
OK I got this now thanks to WPF - How to stop TextBox from autosizing?
Trick is to add the following to ListView definition:
ScrollViewer.HorizontalScrollBarVisibility="Disabled"

How to have a click-able button in my combo-box ItemTemplate?

This is what i have so far:
<dxe:ComboBoxEdit Name="cboUserCustomReports"
Width="300" Height="Auto"
Margin="0,5,0,5"
ItemsSource="{Binding Path=UserReportProfileList,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
EditValue="{Binding Path=UserReportProfileID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ValueMember="UserReportProfileID"
DisplayMember="ReportName"
PopupClosed="cboUserCustomReports_PopupClosed">
<dxe:ComboBoxEdit.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding ReportName, Mode=Default}"
VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
<Button Name="btnDelete"
Grid.Column="1"
Width="20" Height="20"
VerticalAlignment="Center" HorizontalAlignment="Right"
Click="btnDelete_Click">
<Button.Template>
<ControlTemplate>
<Image Source="/RMSCommon;component/Resources/Delete.ico"></Image>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</DataTemplate>
</dxe:ComboBoxEdit.ItemTemplate>
</dxe:ComboBoxEdit>
First, i want the two columns to be stand alone. The user must be able to select or delete the item.
Second, i would like to make my button in the ItemTemplate to be click-able.
What do i need to add to get this behavior?
This is what it looks like at the moment:
Click
I assume, that your button is clickable, and you want to know how to process the click event. Right?
For the click-handler, add the following code:
private void btnDelete_Click(object sender, RoutedEventArgs e) {
FrameworkElement fe = sender as FrameworkElement;
if(null == fe){
return;
}
UserReportProfile userReportProfile = fe.DataContext as UserReportProfile;
if (null == userReportProfile) {
return;
}
// Do here your deletion-operation
}
I assumed that your item-class is named UserReportProfile. Otherwise, change the declared type accordingly.
Layout
For the alignment, add the following declaration to your ComboBox:
HorizontalContentAlignment="Stretch"
This gives your DataTemplate-Grid the full width and you can layout then your items as you desire.
<dxe:ComboBoxEdit Name="cboUserCustomReports"
HorizontalContentAlignment="Stretch"
Width="300" Height="Auto"
Margin="0,5,0,5"
...>
Your question is not clear enough. But I guess you want to vertically align the text and images in your combobox. If so, then all you need to do this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
And I think your items are already clickable!

WPF buttons same/recommended width

Suppose you have a window with multiple buttons such as Ok/Cancel or Yes/No/Cancel. All the buttons need to be the same width. Obviously this could be done by just guessing a number and hardwiring all of them to that number.
Is there a better way to do it, one that would take into account preferred/recommended sizes (just how wide should an Ok button be anyway? This is not a rhetorical question, I actually don't know the answer!), what's needed by the text of the longest caption, what happens if the font size is increased etc?
Another, perhaps simpler, way to do this is to use the SharedSizeGroup property on the ColumnDefinition and RowDefinition classes.
Columns (and Rows) in a WPF Grid can automatically resize to fit their contents - when SharedSizeGroup is used, columns with the same group name share their resizing logic.
The Xaml would look something like this ...
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Ok"
Margin="4" />
<Button Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Cancel"
Margin="4" />
<Button Grid.Column="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Long Button Caption"
Margin="4" />
</Grid>
There are several ways to do this:
1) Use a Grid for layout. Each Button gets its own Column, which is Star-sized. That way, all columns are the same size:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Yes</Button>
<Button Grid.Column="1">No</Button>
<Button Grid.Column="2">Cancel</Button>
</Grid>
2) You can have one item as "master size" and bind the width of all others to this item's width.
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton" Width="100">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="Width"/>
</Button.Width>
No
</Button>
</StackPanel>
EDIT: In actual code, you probably will have Width="Auto". Since the other widths are based on the "master width", the button with the widest width (widest text) should be chosen.
Use a "master" control, like in Daniel's answer, but bind to the "ActualWidth" attribute instead of "Width":
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="ActualWidth"/>
</Button.Width>
No
</Button>
</StackPanel>
This way, the value is taken from the master control at run time, after minimum and maximum width and all other layout calculations have been taken into account. Binding to "Width" binds to whatever you happen to put into the attribute at compile time, which may not be the width that is really used.
Also, the binding can be written shorter like
<Button Width="{Binding ElementName=MasterButton, Path=ActualWidth}"/>
According to the MS User Experience Interaction Guidelines for Windows 7 and Windows Vista (p61), standard dimensions for command buttons are 50x14 DLU actual size (75x23 pixels). The guidelines further suggest you "try to work with [these] default widths and heights." Obviously, if you need more width to fit a clear label, then take more width.
These answers are great if you have a fixed number or fixed layout for the buttons, but if like me there is a dynamic number of buttons coming from a binding and contained in a ItemsControl then this is not feasible. But there is a simple way and it still involves used the sharedsize property of Grid.
DataTemplate:
<DataTemplate x:Key="ODIF.Mapping">
<Button HorizontalContentAlignment="Left" Background="#FFEEEEEE" BorderBrush="#FFBDBDBD">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="PluginButtonsWidth"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsIconHeight"/>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsNameHeight"/>
</Grid.RowDefinitions>
<Image Width="32" Height="32" Source="{Binding PluginIcon}" RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Grid.Row="1" Text="{Binding PluginName}"/>
</Grid>
</Button>
</DataTemplate>
Parent container:
<ItemsControl ItemsSource="{Binding MappingPlugins, ElementName=page}" ItemTemplate="{StaticResource ODIF.Mapping}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Essentially the button's content can itself be a Gird which then you can place your labels and icons as needed in, but even though the buttons do not reside in the same grid (they each are their own) the grid can still share it size so long as you set the root container's (ItemsControl) property of Grid.IsSharedSizeScope to True.
This will force the content grid of each button to be the same exact size based on the largest one while not having to have the Buttons themselves in a predefined grid.
In the most general case, you want to create a
Style in your section, then apply this style as desired. Now when you change the style, all buttons change.
Or you can change the Content of the button so that it autosizes to the text.

Resources