Swap vertical ScrollBar side based on window position on screen - wpf

I have a window that contains a ScrollViewer, What i want is to swap the vertical ScrollBar side to the left if the window is on the right side of the screen and vice-versa.
Here is my current ScrollViewer template in a ResourceDictionary:
<Style x:Key="ScrollViewerWithoutCollapsedVerticalScrollBar" TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="0" BorderThickness="0">
<ScrollContentPresenter />
</Border>
<ScrollBar x:Name="PART_VerticalScrollBar"
Grid.Column="1"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility, Converter={StaticResource ComputedScrollBarVisibilityWithoutCollapse}}" />
<ScrollBar x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="0"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What would be the way to go on from there?

Positioning the ScrollBar inside of your ScrollViewer depending on the window position requires you to know:
The Size of the containing window to know where the center is
The window location to determine whether it crossed the center of the screen or not
The screen size to know where the center of the screen even is
What makes it addionally difficult are the following factors
You need to get the information from your ScrollViewer that is a child of the window that could change
Size changes and location changes can also change the side of the screen
Screen sizes are difficult to get in WPF, especially in multi-monitor setups
I will show you a working example to achieve what you want for the primary screen. Since this is another block for a another question, you can start from there and adapt it to your requirements.
In order to tackle the issues above, we will use the SizeChanged and LocationChanged events to detect changes of a window in size and location. We will use the SystemParameters.PrimaryScreenWidth to get the screen width, which can, but may not work in multi-monitor setups with different resolutions.
Your control will alter the default ScrollViewer behavior and appearance. I think it is best to create a custom control to make it reusable, because dealing with this in XAML with other techniques might become messy.
Create the custom scroll viewer
Create a new type AdaptingScrollViewer that inherits from ScrollViewer like below. I have commented the code for you to explain how it works.
public class AdaptingScrollViewer : ScrollViewer
{
// We need this dependency property internally, so that we can bind the parent window
// and get notified when it changes
private static readonly DependencyProperty ContainingWindowProperty =
DependencyProperty.Register(nameof(ContainingWindow), typeof(Window),
typeof(AdaptingScrollViewer), new PropertyMetadata(null, OnContainingWindowChanged));
// Getter and setter for the dependency property value for convenient access
public Window ContainingWindow
{
get => (Window)GetValue(ContainingWindowProperty);
set => SetValue(ContainingWindowProperty, value);
}
static AdaptingScrollViewer()
{
// We have to override the default style key, so that we can apply our new style
// and control template to it
DefaultStyleKeyProperty.OverrideMetadata(typeof(AdaptingScrollViewer),
new FrameworkPropertyMetadata(typeof(AdaptingScrollViewer)));
}
public AdaptingScrollViewer()
{
// Relative source binding to the parent window
BindingOperations.SetBinding(this, ContainingWindowProperty,
new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Window), 1) });
// When the control is removed, we want to clean up and remove the event handlers
Unloaded += OnUnloaded;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
RemoveEventHandlers(ContainingWindow);
}
// This method is called when the window in the relative source binding changes
private static void OnContainingWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var scrollViewer = (AdaptingScrollViewer)d;
var oldContainingWindow = (Window)e.OldValue;
var newContainingWindow = (Window)e.NewValue;
// If the scroll viewer got detached from the current window and attached to a new
// window, remove the previous event handlers and add them to the new window
scrollViewer.RemoveEventHandlers(oldContainingWindow);
scrollViewer.AddEventHandlers(newContainingWindow);
}
private void AddEventHandlers(Window window)
{
if (window == null)
return;
// Add events to react to changes of the window size and location
window.SizeChanged += OnSizeChanged;
window.LocationChanged += OnLocationChanged;
// When we add new event handlers, then adapt the scrollbar position immediately
SetScrollBarColumn();
}
private void RemoveEventHandlers(Window window)
{
if (window == null)
return;
// Remove the event handlers to prevent memory leaks
window.SizeChanged -= OnSizeChanged;
window.LocationChanged -= OnLocationChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
SetScrollBarColumn();
}
private void OnLocationChanged(object sender, EventArgs e)
{
SetScrollBarColumn();
}
private void SetScrollBarColumn()
{
if (ContainingWindow == null)
return;
// Get the column in the control template grid depending on the center of the screen
var column = ContainingWindow.Left <= GetHorizontalCenterOfScreen(ContainingWindow) ? 0 : 2;
// The scrollbar is part of our control template, so we can get it like this
var scrollBar = GetTemplateChild("PART_VerticalScrollBar");
// If someone overwrote our control template and did not add a scrollbar, ignore
// it instead of crashing the application, because everybody makes mistakes sometimes
scrollBar?.SetValue(Grid.ColumnProperty, column);
}
private static double GetHorizontalCenterOfScreen(Window window)
{
return SystemParameters.PrimaryScreenWidth / 2 - window.Width / 2;
}
}
Creating a control template
Now our new AdaptingScrollViewer nees a control template. I took your example and adapted the style and control template and commented the changes, too.
<!-- Target the style to our new type and base it on scroll viewer to get default properties -->
<Style x:Key="AdaptingScrollViewerStyle"
TargetType="{x:Type local:AdaptingScrollViewer}"
BasedOn="{StaticResource {x:Type ScrollViewer}}">
<Setter Property="Template">
<Setter.Value>
<!-- The control template must also target the new type -->
<ControlTemplate TargetType="{x:Type local:AdaptingScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<!-- Added a new column for the left position of the scrollbar -->
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Column="1" BorderThickness="0">
<ScrollContentPresenter/>
</Border>
<ScrollBar x:Name="PART_VerticalScrollBar"
Grid.Row="0"
Grid.Column="2"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"/>
<!-- Added a column span to correct the horizontal scroll bar -->
<ScrollBar x:Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to add the following style after the style above in your resource dictionary, too, so that the AdaptingScrollViewer get styled automatically.
<Style TargetType="{x:Type local:AdaptingScrollViewer}" BasedOn="{StaticResource AdaptingScrollViewerStyle}"/>
Showing the result
In in your main XAML create the control like this to enable both scrollbars and see the result.
<local:AdaptingScrollViewer HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"/>

All you need to do in order to put a scrollbar on the left of a scrollviewer rather than right is to set the flowdirection to RightToLeft.
You will then need to explicitly set the flowdirection to lefttoright on a container within that to stop it affecting contents.
Here's some experimental markup to consider:
<Grid>
<ScrollViewer FlowDirection="RightToLeft"
Name="sv"
>
<StackPanel FlowDirection="LeftToRight">
<TextBlock Text="Banana"/>
<TextBlock Text="Banana"/>
<TextBlock Text="Banana"/>
</StackPanel>
</ScrollViewer>
<ToggleButton HorizontalAlignment="Center" VerticalAlignment="Top"
Click="ToggleButton_Click"
Content="Flow"
/>
</Grid>
</Window>

Related

I want to continuous display of buttons in wpf

In my application I have 4 button. If user will click on a button, that will disappear and remaining 3 still display.
The problem is that I don't want a control (button) gap between button.
I want remaining 3 should rearrange itself.
Which control I should use or how I can implement that. I am using MVVM in my application.
Button and use Visibility = Collapsed
There are multiple ways to achieve what you need. You have to be specific with you question. My way of doing this would be
xaml:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="Button_Click"/>
</Style>
</StackPanel.Resources>
<Button Content="I am the first buttom" Margin="5"/>
<Button Content="I am the second button" Margin="5"/>
<Button Content="I am the third button" Margin="5"/>
</StackPanel >
CodeBehind:
public void Button_Click(object sender, EventArgs e)
{
(sender as Button).Visibility = Visibility.Collapsed;
}
Summary of the Solution: I would use a Grid layout with automatically expanding ColumnDefinitions (the default setting of Width="1*". When the button is clicked and disappears (Use Opacity="0"), you should change the width of the corresponding ColumnDefinition to 0.
Here are the before and after screen shots of what you want:
Before
After Click Donkey Button
After Click Bird Button
After Click Moose Button
After Click Giraffe Button(I accidentally resized before I screenshotted)
I know that you are using MVVM, but this problem is entirely in the user interface, so its encouraged to use code-behind for this solution. I don't recommend putting this type of code into mvvm.
For this solution, use a grid:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
Title="Used for question 36258073" >
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="AllButton_Click"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="ColumnDefinitionNull"/>
<ColumnDefinition Name="ColumnDefinitionOne"/>
<ColumnDefinition Name="ColumnDefinitionTwo"/>
<ColumnDefinition Name="ColumnDefinitionThree"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Name="ButtonNull" Content="Test1 Giraffe" Grid.Row="0" Grid.Column="0" />
<Button Name="ButtonOne" Content="Test2 Bird" Grid.Row="0" Grid.Column="1" />
<Button Name="ButtonTwo" Content="Test3 Donkey" Grid.Row="0" Grid.Column="2" />
<Button Name="ButtonThree" Content="Test4 Moose" Grid.Row="0" Grid.Column="3" />
</Grid>
and the code-behind for the button click is:
private void AllButton_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
button.Opacity = 0;
var zeroWidth = new GridLength(0.0);
switch (button.Name)
{
case "ButtonNull":
ColumnDefinitionNull.Width = zeroWidth;
break;
case "ButtonOne":
ColumnDefinitionOne.Width = zeroWidth;
break;
case "ButtonTwo":
ColumnDefinitionTwo.Width = zeroWidth;
break;
default:
ColumnDefinitionThree.Width = zeroWidth;
break;
}
}
The idea for the Opacity 0 came from another Stack Overflow post here. The notion about the buttons stretching in place of the other button disappearring comes from button layout and grid definition ideas this Stack Overflow post1, and this Stack Overflow post2, and this Stack Overflow post3 and this last related Stack Overflow post4.

wrapping a data grid with a toolbox

I got a wpf application.
I want all my data grids in application to have a set of buttons above them.
Tried to use decorator and adorner without success(the dataGrid stopped showing rows)
Any suggestions?
Given that you're wanting to have functionality behind the toolbox buttons (which I assume will require a reference to the grid) it probably makes sense to inherit from a HeaderedContentControl for this. This does mean that you can put any content in the control, but it would be possible to put override the metadata to add validation for this.
Anywhere, here's the xaml:
<!-- ToolBoxGridControl.xaml -->
<HeaderedContentControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication3.ToolBoxGridControl">
<HeaderedContentControl.Header>
<StackPanel Orientation="Horizontal">
<Button/>
<Button/>
<Button/>
</StackPanel>
</HeaderedContentControl.Header>
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{TemplateBinding Header}"/>
<ContentControl Grid.Row="1" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</HeaderedContentControl.Template>
</HeaderedContentControl>
And the simple code-behind (where you can put your toolbox implementation).
public partial class ToolBoxGridControl : HeaderedContentControl
{
private DataGrid DataGrid { get { return (DataGrid)Content; } }
public ToolBoxGridControl()
{
this.InitializeComponent();
}
}
To actually use, you can just add the following to your XAML with your data grid
<local:ToolBoxGridControl>
<DataGrid/>
</local:ToolBoxGridControl>

Prevent a TextBox from horizontal expanding in WPF

I have the following style defined in my App.xaml
<Style x:Key="textBoxMultiline" TargetType="{x:Type TextBox}" >
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="MinHeight" Value="50" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
And throughout the solution we're using it on every text box that needs a brief text.
<TextBox x:Name="textBoxDescription" Grid.Row="2" Grid.Column="1" Style="{DynamicResource textBoxMultiline}" />
Everything works great, but then the client complains about some fields were corped on older monitors with lower resolutions, so I placed a ScrollViewer on one of the higher visual tree nodes to prevent the corping.
<ScrollViewer Height="Auto" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
...
</ScrollViewer>
Strangely, the TextBoxes with the above style start expanding to the right instead of wrapping the text.
Is there a way to prevent this without removing the ScrollViewer?
If you don't want to hard code the width then you can go for element binding the width of the parent item.
Here I am binding TextBox MaxWidth with ScrollViewer actual width. You also have to make sure that the ColumnDefinition width should be set to "*" not to "Auto". If you set it to Auto it will neglect the ScrollViewer width and keep on expanding the width of ScrollViewer and TextBox. I think you fall in this case...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" Name="scv">
<TextBox Height="30" TextWrapping="Wrap" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}"></TextBox>
</ScrollViewer>
</Grid>
You must define a MaxWidth for the TextBox, otherwise there's no limit because the ScrollViewer.
The solution provided from #bathineni helped me solve my problem. Here is what worked for me:
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Width="30" Height="23" Margin="10,5" Content="..."/>
<ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Disabled" verticalScrollBarVisibility="Disabled" Name="scv">
<TextBox Height="25" Text="Insert here long text" MaxWidth="{Binding ElementName=scv, Path=ActualWidth}" HorizontalAlignment="Stretch" />
</ScrollViewer>
</Grid>
I tried the aforementioned examples and they didn't work so, I solved the problem myself. There are two ways of solving this issue:
The first solution is implemented in XAML using data bindings. I advice you not to bind the control by itself. The XAML solution is implemented by binding a control with the desired ActualWidth and ActualHeight proprieties to the textbox MaxHeight and MaxWidth proprieties.
<TextBlock x:Name="PasswordText" Margin="0,0,0,20" FontFamily="Bahnschrift SemiBold Condensed" Text="PASSWORD" FontSize="20">
<TextBox x:Name="PasswordTextBox" MaxWidth="{Binding ElementName=PasswordText, Path=ActualWidth}" MaxHeight="{Binding ElementName=PasswordText, Path=ActualHeight}">
The next solution is implemented by generating a Loaded event in XAML, creating it in the C# code and then setting within the Loaded event the MaxWidth and MaxHeight proprieties of the textbox as the textbox ActualWidth and ActualHeight proprieties.
// It doesn't have a problem like in XAML if you pass the textbox its own
// ActualWidth and ActualHeight to the MaxWidth and MaxHeight proprieties.
private void Log_In_Page_Loaded(object sender, RoutedEventArgs e)
{
UsernameTextBox.MaxHeight = UsernameTextBox.ActualHeight;
UsernameTextBox.MaxWidth = UsernameTextBox.ActualWidth;
}
Choose the one that suits your design better, but I think, in my opinion, that this is the most effective, simple, stable and effective way of solving this problem.
Works for me. If you want scrollbars to appear in the textbox, you may add
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
to the TextBox
You must set MaxWidth of the Container Control
<Grid x:Name="RootGrid" Margin="6,6,8,8" Width="500" MaxWidth="500">
<ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Name="contentGroup" Header="Content" Grid.Row="0">
<TextBox Name="content"/>
</GroupBox>
</Grid>
</ScrollViewer>
I ran into this problem when I needed my TextBox to stretch along with its auto-sized Grid column when it got resized, meaning MaxWidth wouldn't work, but still needed to prevent the TextBox from stretching along with its contents.
What I ended up doing was linking this event handler to SizeChanged:
private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e) {
TextBox textBox = (TextBox)sender;
if (textBox.CanUndo && e.NewSize.Width > e.PreviousSize.Width) {
textBox.Width = e.PreviousSize.Width;
}
}
Writing text to a textbox is something that the user can Undo, whereas other actions that can cause resizing (initial drawing of the element, stretching of parent container, etc) aren't Undoable from within the textbox. Thus, by checking CanUndo we can determine whether SizeChanged was triggered by writing text or something else.
The e.NewSize.Width > e.PreviousSize.Width check is necessary because without it the SizeChanged event will infinitely be called from within itself, because to revert the stretching we need to change the size back to the original, which would itself trigger the event.
It's a little hacky but I haven't run into any issues yet.
I am not sure why but I could not get the ScrollViewer solution to work. I needed to have a TextBox with a fixed initial width to implement a numeric up/down control - in this control the TextBox was shrinking and growing independent of the input which looks very annoying if the UI changes as you type.
So, I found the below solution using 2 textboxes to work for me. The first textbox is the textbox displayed for the user to type their input and the 2nd textbox is initialized through a dependency property (DisplayLength) and the converter shown further below.
Binding the MaxWidth property of the 1st TextBox to the Width property of the 2nd TextBox fixes the size such that users can type what they want but the displayed width of the textbox will not change even if there is more UI space available.
<TextBox x:Name="PART_TextBox"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}"
Margin="0,0,1,0"
TextAlignment="Right"
AcceptsReturn="False"
SpellCheck.IsEnabled="False"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MaxWidth="{Binding ElementName=TBMeasure, Path=ActualWidth}"
/>
<!-- Hidden measuring textbox ensures reservation of enough UI space
according to DisplayLength dependency property
-->
<TextBox x:Name="TBMeasure"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DisplayLength, Converter={StaticResource ByteToPlaceHolderStringConverter}}"
Margin="0,0,1,0"
TextAlignment="Right"
AcceptsReturn="False"
SpellCheck.IsEnabled="False"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="Hidden"/>
// Converter
[ValueConversion(typeof(byte), typeof(string))]
public sealed class ByteToPlaceHolderStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((value is byte) == false)
return Binding.DoNothing;
byte byteVal = (byte)value;
string retString = string.Empty;
for (int i = 0; i < byteVal; i++)
retString = retString + "X";
return retString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

cloning the controls

I am designing a silverlight application in which i will have a rectangle control at the left side, when i click the rectangel and drag a copy of the rectangle control should be created and dragged and dropped in to the page.
Please can anyone help me with the code
For simplicity I'm going to leave out the Drag-Drop stuff since this question seems mainly about the cloning aspect.
The tool needed is the DataTemplate class. You place in a resource dictionary the set of items you want to clone each enclosed in a DataTemplate. You can use ContentPresenter to display instances of these items in say stack panel on the left. You can then use code to create instances of the template content and place them in say a Canvas on the right.
Example.
Xaml:-
<UserControl x:Class="SilverlightApplication1.CloningStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.Resources>
<DataTemplate x:Key="Rectangle">
<Rectangle Stroke="Blue" StrokeThickness="3" Fill="CornflowerBlue" Width="100" Height="75" />
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel>
<ContentPresenter x:Name="Rectangle" ContentTemplate="{StaticResource Rectangle}" />
</StackPanel>
<Canvas x:Name="Surface" MouseLeftButtonDown="Surface_MouseLeftButtonDown" Grid.Column="1" Background="Wheat">
</Canvas>
</Grid>
</UserControl>
Code:-
public partial class CloningStuff : UserControl
{
public CloningStuff()
{
InitializeComponent();
}
private void Surface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Canvas target = (Canvas)sender;
Point p = e.GetPosition(target);
Rectangle r = (Rectangle)((DataTemplate)Resources["Rectangle"]).LoadContent();
Canvas.SetLeft(r, p.X);
Canvas.SetTop(r, p.Y);
target.Children.Add(r);
}
}
This shows using a ContentPresenter to display your rectangle. In place of drag-dropping (for which there are plenty of examples of elsewhere) this code just creates a Clone of the rectangle whereever the user clicks in the Canvas.

Silverlight Scrollviewer With Only Buttons

I am using a ScrollViewer as part of my Silverlight application. It has a horizontal orientation, and I would like it to appear such that only the scroll buttons appear, but not the scroll bar itself. Something like this crude ASCII rendering:
------------------------------------------------------
| | | |
| < | Content Here | > |
| | | |
------------------------------------------------------
I know I could use the templating functionality, but all the samples I've seen only change the look of all the elements, and not their raw positioning, or whether they even appear. Is it possible to do this, and could someone provide an outline of what the template might look like?
Here is another option. Override the default template for SCrollviewer and handle the buttons as PageUp/PageDown. My example below is a scrollviewer that scrolls vertically. You can easily change to to horizontal scrolling and change the handlers from PageUp/PageDown to Left and Right handlers.
<ControlTemplate TargetType="{x:Type ScrollViewer}" x:Key="ButtonOnlyScrollViewer">
<ControlTemplate.Resources>
<!-- Add style here for repeat button seen below -->
</ControlTemplate.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0"
Foreground="White"
Background="Yellow"
HorizontalAlignment="Stretch"
Command="ScrollBar.PageUpCommand"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}">
</RepeatButton>
<ScrollContentPresenter
CanContentScroll="{TemplateBinding CanContentScroll}"
Grid.Row="1"
Content="{TemplateBinding Content}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Margin="{TemplateBinding Margin}"/>
<RepeatButton Grid.Row="2" Background="Black" Foreground="White" Command="ScrollBar.PageDownCommand">
</RepeatButton>
</Grid>
</ControlTemplate>
I've done something similar and the best way I found to do this was to put your content in a scroll viewer and just turn off the scrollbars. Then code your buttons to scroll the scrollviewer.
Edit: Responding to comment about no way to deal with sizing.
First off, you would build this control as a ContentControl. It should have a template defined in generic.xaml that has your button controls plus the scroll viewer. Something like:
<Canvas x:Name="root">
<Button x:Name="left" Content="<"/>
<Button x:Name="right" Content=">"/>
<ScrollViewer x:Name="viewer" BorderThickness="0" VerticalScrollBarVisibility="Hidden">
<ContentPresenter />
</ScrollViewer>
</Canvas>
Then in your control you would need to override OnApplyTemplate:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
left = GetTemplateChild("left") as Button;
left.Click += new RoutedEvent(YourHandler);
right = GetTemplateChild("right") as Button;
right.Click += new RoutedEvent(YourHandler);
// position your scroll buttons here, not writing that code
scroll = GetTemplateChild("viewer") as ScrollViewer;
root = GetTemplateChild("root") as Canvas;
var fe = this.Content as FrameworkElement;
if (fe != null)
{
fe.SizeChanged += new SizeChangedEventHandler(fe_SizeChanged);
}
}
void fe_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.InvalidateMeasure();
}
protected override Size ArrangeOverride(Size finalSize)
{
if (!double.IsInfinity(scroll.ViewportHeight))
{
left.Visibility = (scroll.HorizontalOffset > 0);
right.Visibility = (scroll.HorizontalOffset < ScrollableHeight);
}
return base.ArrangeOverride(finalSize);
}
protected override Size MeasureOverride(Size availableSize)
{
scroll.Measure(availableSize);
return scroll.DesiredSize;
}
In your button click handlers you would need to (1) scroll the viewer and (2) check the new value of the HorizontalOffset to see if you need to hide or show either of the button.
Disclaimer: This code probably doesn't work as is since it was written by hand and based on a different example.
i found the solution here :)
http://weblogs.asp.net/fredriknormen/archive/2009/09/18/create-an-automatic-scrollable-image-slider-in-silverlight.aspx
This is made using a DispatcherTimer, really nice example :)
I have been searching for working solution for quite a lot of time now. And based on Louis's solution I have managed to make it working. (in WPF)
This solution is for horizontal scrolling.
Firstly, add ListView:
<ListView ItemsSource="{Binding Items}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate>
<ScrollViewer Template="{StaticResource ButtonOnlyScrollViewer}">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListView.Template>
</ListView>
And a modified template from Louis's answer for horizontal scrolling:
<ControlTemplate TargetType="{x:Type ScrollViewer}" x:Key="ButtonOnlyScrollViewer">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<RepeatButton Content="<"
Grid.Column="0"
Command="ScrollBar.LineLeftCommand"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<ScrollContentPresenter
CanContentScroll="{TemplateBinding CanContentScroll}"
Grid.Column="1"
Content="{TemplateBinding Content}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Margin="{TemplateBinding Margin}"/>
<RepeatButton Content=">"
Grid.Column="2"
Command="ScrollBar.LineRightCommand"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>

Resources