Stop WPF ScrollViewer automatically scrolling to perceived content - wpf

The Application
I am building an application which includes a range selector. This consists of two custom drawn Slider controls contained within one UserControl derived class. The range selector control is then contained inside a ScrollViewer which has the HorizonalScrollBar visible most of the time.
Sample Application Code: ( appologies for the wall of text )
Window.xaml ( the Window file ):
<Grid>
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
<local:SliderTest x:Name="slider"
LowerValue="0"
UpperValue="10"
Minimum="0"
Maximum="100" Width="900" Height="165" Padding="15,0,15,0" HorizontalAlignment="Left">
</local:SliderTest>
</ScrollViewer>
</Grid>
SliderTest.xaml:
<UserControl x:Class="scrollviewerDemoProblem.SliderTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Name="root"
xmlns:local="clr-namespace:scrollviewerDemoProblem"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<ControlTemplate x:Key="simpleSlider" TargetType="{x:Type Slider}">
<Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Track x:Name="PART_Track" Grid.Row="1">
<Track.Thumb>
<Thumb x:Name="Thumb" FlowDirection="LeftToRight" Width="15">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Canvas>
<Path x:Name="test1" StrokeThickness="0" Fill="DarkGreen">
<Path.Data>
<GeometryGroup FillRule="NonZero">
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="True" StartPoint="0,150" IsFilled="True">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="-15,150" />
<LineSegment Point="-15,0" />
<LineSegment Point="0,0" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
</Canvas>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
<ControlTemplate x:Key="simpleSliderRight" TargetType="{x:Type Slider}">
<Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Track x:Name="PART_Track" Grid.Row="1">
<Track.Thumb>
<Thumb x:Name="Thumb" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Width="15">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Canvas>
<Path Stroke="Black" StrokeThickness="0" Fill="DarkCyan">
<Path.Data>
<GeometryGroup FillRule="NonZero">
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="True" StartPoint="0,150">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="15,150" />
<LineSegment Point="15,0" />
<LineSegment Point="0,0" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
</Canvas>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</UserControl.Resources>
<Grid x:Name="Gridd" VerticalAlignment="Top" Height="165" >
<Border x:Name="timeScaleBorder" Width="auto" Height="15" VerticalAlignment="Top" Background="Black">
<Canvas x:Name="timeCanvas" Width="auto" Height="15">
</Canvas>
</Border>
<Border x:Name="background" BorderThickness="1,1,1,1" BorderBrush="Black" VerticalAlignment="Center" Height="150"
Margin="0,15,0,0" Background="White" />
<Slider x:Name="LowerSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=LowerValue, Mode=TwoWay}"
Template="{StaticResource simpleSlider}"
Margin="0,15,0,0" />
<Slider x:Name="UpperSlider"
Minimum="{Binding ElementName=root, Path=Minimum}"
Maximum="{Binding ElementName=root, Path=Maximum}"
Value="{Binding ElementName=root, Path=UpperValue, Mode=TwoWay}"
Template="{StaticResource simpleSliderRight}"
Margin="0,15,0,0" />
</Grid>
</UserControl>
SliderText.xaml.cs:
public partial class SliderTest : UserControl
{
public SliderTest()
{
InitializeComponent();
}
#region Dependency properties, values etc.
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));
public double LowerValue
{
get { return (double)GetValue(LowerValueProperty); }
set { SetValue(LowerValueProperty, value); }
}
public static readonly DependencyProperty LowerValueProperty =
DependencyProperty.Register("LowerValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));
public double UpperValue
{
get { return (double)GetValue(UpperValueProperty); }
set { SetValue(UpperValueProperty, value); }
}
public static readonly DependencyProperty UpperValueProperty =
DependencyProperty.Register("UpperValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));
public double Maximum
{
get { return (double)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(1d));
public double Minimum
{
get { return (double)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
#endregion
}
The Problem
Most of the sample code provided is boring and the mechanics of it works pretty good. The problem I am having is a visual problem specifically with the ScrollViewer control that I have in the main Window. The ScrollViewer seems to be automatically adjusting the horizontal offset of the ScrollViewer when either of the Slider's gains focus ( from a mouse click for example ).
Reproducing the behaviour
Run the application, you will see that the horizontal scroll bar of the ScrollViewer is visible.
Click on the Green ( far left ) Slider, you will notice that the ScrollViewer automatically adjusts to shift the horizontal offset to where the perceived 'content' starts.
These symptoms occur at either end of the scroll pane.
Screenshot of application when it is run ( Application is Zoomed in 200% for detail clarity ):
Screenshot of the behavior when the left slider is clicked:
What I want to happen:
When I click on either slider item ( at either end ) when a slider looks to be beyond end of the slider ( slider range is denoted by the black bar at the top ) I don't want the ScrollViewer to automatically adjust it's horizontal offset.
Suspected problem:
I suspect that the problem is that the ScrollViewer perceives the actual 'content' of it's childen starts 15 pixels ( the drawn width of both of my sliders ) in from where the actual drawn content does start. The Canvas only draws because I included a padding of 15 pixels inside of the SliderTest control on the main window, if this padding is removed the ScrollViewer does not show any of the Slider's Canvas.
EDIT : it appears the padding is not the problem, read the comments as to why.
Things I have tried
I have tried looking into overriding the OnPreviewMouseDown event of the main Window. The problem here is that I still want both Slider's to behave normally, setting the event to Handled causes the Slider to stop working completely.
Notes:
The Slider's within the range selector control ( Called SliderTest in this example ) must both have a width of 1 pixel. The slider's must be able to extend 15 pixels past the end of the time selection range ( see the black bar at the top for a reference ).
Thank you for reading this novel-lengthed problem.

By default when a control receives the logical focus, FrameworkElement calls its own BringIntoView method (from within its OnGotFocus method if it has keyboard focus). That results in a RequestBringIntoView event being generated that bubbles up the element tree to allow ancestor elements to bring that portion of the element into view. The ScrollViewer listens for this event and eventually will call MakeVisible on the associated IScrollInfo/ScrollContentPresenter which leaves it up to the panel to bring that portion into view (since the panel would know how it arranges its children). It then takes that returned rect it receives back and asks for that portion of itself to be brought into view (in case you had nested elements that would require some action to ensure the original element was brought into view). So one way to suppress this behavior would be to handle the RequestBringIntoView event on the sliders and mark the event handled.

This may not work in this specific scenario, but a simple, clean solution to prevent a ScrollViewer from scrolling a focused element into view is to make the element unfocusable via Focusable=False. If an element cannot be focused then it will also not be automatically scrolled into view.

Related

Can width of TextBox be set using FormattedText.MinWidth?

Banging my head trying to figure this out. The goal is to create a wrapping TextBox at a given location where the width is set to that of the largest word in the string of text. FormattedText.MinWidth calculates the width of the largest word in the string. But passing that MinWidth to the TextBox causes the TextBox to be slightly too narrow. TextBlock does not have this problem.
Evidentally something is happening deep down inside the TextBox causing this behavior. I can't just add a fixed magic number to the TextBox width because the increase in the width needed to correct the problem will always differ based on the pixel width of the character that was wrapped to the next line. The number of pixels will always differ depending on what that character is, the font, and font size.
If someone has more reputation could you please add FormattedText and MinWidth as a tag? Restrictions won't let me, a stupid first post newbe, do this. I also would like to have added an image which would make this sooo much easier to understand but stupid restrictions (did I say that?) prevent me from doing so.
namespace FormattedTextExample
{
public partial class FormattedText1 : Window
{
string noteText =
"We hold these truths to be self-evident, that all men are created equal, that they " +
"are endowed by their Creator with certain unalienable Rights, that among these are " +
"Life, Liberty and the pursuit of Happiness.--That to secure these rights, Governments " +
"are instituted..";
public FormattedText1()
{
InitializeComponent();
myText.Text = noteText;
}
protected override void OnRender(DrawingContext drawingContext)
{
FormattedText ft = new FormattedText(
textToFormat: noteText,
culture: CultureInfo.GetCultureInfo("en-us"),
flowDirection: myText.FlowDirection,
typeface: new Typeface(myText.FontFamily.ToString()),
emSize: myText.FontSize,
foreground: myText.Foreground);
ft.MaxTextWidth = ft.MinWidth;
DrawingContext dc = drawDest.Open();
dc.DrawText(ft, new Point(0, 0));
dc.Close();
myDrawingBrush.Drawing = drawDest;
myText.Width = ft.MinWidth;
}
}
}
<Window x:Class="FormattedTextExample.FormattedText1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="FormattedText" Height="500" Width="500">
<DockPanel>
<Grid DockPanel.Dock="Top" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0">
<Rectangle.Fill>
<DrawingBrush x:Name="myDrawingBrush" Stretch="None"
AlignmentY="Top" AlignmentX="Left" >
<DrawingBrush.Drawing>
<DrawingGroup x:Name="drawDest" />
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel Grid.Column="1">
<TextBox x:Name="myText" TextWrapping="Wrap" />
<!-- Everything works fine if using TextBlock -->
<!--<TextBlock x:Name="myText" TextWrapping="Wrap" />-->
</StackPanel>
</Grid>
</DockPanel>
</Window>
The reason your textbox needs more space is the way its control template is defined.
This is how the default control template looks like (from MSDN):
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border"
BorderThickness="1"
CornerRadius="2"
Padding="2">
<Border.Background>
<SolidColorBrush Color="{DynamicResource ControlLightColor}" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<!-- VisualStateManager code -->
<ScrollViewer x:Name="PART_ContentHost" Margin="0" />
</Border>
</ControlTemplate>
Studying the default control template or defining your own helps determining exactly how much extra space do you need to allocate to your text box.

How do I create a custom pushpin with image, label, and click event?

I am working on a location aware app, where I want to have custom pushpins that have an image, and when you tap the image, a label is added. I have tried a couple of solutions...
I started with this code, from this article: http://igrali.wordpress.com/2011/12/06/making-a-custom-windows-phone-bing-pushpin-from-an-image/
<ControlTemplate
x:Key="PushpinMe"
TargetType="maps:Pushpin">
<Grid
Name="PushpinMeGrid"
Height="50"
Width="50"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image
x:Name="PushpinMeImage"
Height="50"
Width="50"
Source="Pushpins/pushpinSeaplane.png" />
<TextBlock Text="{Binding Source=}"
</Grid>
</ControlTemplate>
Then I tried wrapping the image in a button, but that just made the pushpin essentially invisible. Then I tried using a control template from one of my prior apps, and modified it, and came up with this:
<Button
Name="PushpinButton"
Click="Button_Click">
<Button.Style>
<Style
TargetType="Button">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="*" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Height="50"
Width="50"
Source="Pushpins/pushpinSeaplane.png" />
<Grid
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition
Height="39" />
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
Background="Black">
<TextBlock
Grid.Row="0"
Foreground="White"
Text="{Binding ElementName=me,
Path=Content}"
TextWrapping="Wrap"
Margin="5" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</ControlTemplate>
Still not a winner - I can't bind the content of the button, and therefore the textblock.
There will be a series of pushpins, with different images, and different labels, so ideally, I would like to come up with a template that I can use, and bind the image and the label from code. The code for the button's click event would be as simple as making the textblock visible or collapsed.
I know my second example is pretty ugly, but I was trying to make the visual look right - I'll modify it as needed for the visuals, but for the moment, I need to figure out how I can bind the image and the text from code. The button click event works with just a messagebox for now (to show that it registered the click event).
Thanks for your assistance.
Sounds like a fun project! I've implemented databinding on nested content controls within a button before using declarations similar to the following. So in your case the push pin collection would be bound to the items control with each push pin object providing the data for its corresponding button (including the button's nested image and textblock).
Let's take a look at a simple example that I hope will guide you in the right direction.
To start here's an example of a button template that you could define in the resource dictionary of your choosing. Note the visibility binding on the image and the text binding on the textblock, these properties will be located on a Pushpin_ViewModel we'll define later:
<Style x:Name="PushpinButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Height="50" Width="50" Source="Pushpins/pushpinSeaplane.png" Visibility="{Binding PushpinImageVisibility}" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="39" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Black">
<TextBlock Grid.Row="0" Foreground="White" Text="{Binding PushpinLabelText}" TextWrapping="Wrap" Margin="5" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In your primary view where your pushpins are displayed you may have some sort of items control responsible for displaying your pushpins. Here is an example of such an items control where the data template is a button with two important features 1) the button style we defined above and 2) a click event that will call a toggle method on the corresponding pushpin view model:
<ItemsControl ItemsSource="{Binding Pushpins}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Name="PushpinButton" Click="pushpinButton_Click" DataContext="{Binding}" Style="{StaticResource PushpinButtonStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void pushpinButton_Click(object sender, RoutedEventArgs e)
{
Pushpin_ViewModel pushpin_ViewModel = ((Button)sender).DataContext as Pushpin_ViewModel;
pushpin_ViewModel.TogglePushpinVisibility();
}
The following view model class would represent the data context of your primary view (the one that contains the items control we defined above). Here we have the collection of pushpins that populate the items control:
public class PrimaryPushpinView_ViewModel : INotifyPropertyChanged
{
public PushpinView_ViewModel()
{
this.Pushpins.Add(new Pushpin_ViewModel() { PushpinLabelText="First Pushpin" });
}
public List<Pushpin_ViewModel> Pushpins
{
get { return pushpins; }
set
{
if (value != pushpins)
{
pushpins = value;
OnPropertyChanged("Pushpins");
}
}
}
private List<Pushpin_ViewModel> pushpins = new List<Pushpin_ViewModel>();
}
And finally here is a representation of the pushpin view model. There would be one instance of this class per pushpin in your pushpin collection:
public class Pushpin_ViewModel : INotifyPropertyChanged
{
public Visibility PushpinVisibility
{
get { return pushpinVisibility; }
set
{
if (value != pushpinVisibility)
{
pushpinVisibility= value;
OnPropertyChanged("PushpinVisibility");
}
}
}
private Visibility pushpinVisibility;
public String PushpinLabelText
{
get { return pushpinLabelText; }
set
{
if (value != pushpinLabelText)
{
pushpinLabelText= value;
OnPropertyChanged("PushpinLabelText");
}
}
}
private String pushpinLabelText;
public void TogglePushpinVisibility()
{
this.PushpinVisibility = this.PushpinVisibility.Equals(Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
}
}
Sorry it took a while to get back to you, crazy day today, hope this helps out.

How to change the foreground of the Thumb of Scrollbar in wpf?

I want to change the foreground of the Scroll bar thumb in my code.
I have applied a style which changes my thumb background, but, i wanted to change the
foreground image at run time. here is my style code for scroll bar.
<ControlTemplate x:Key="MyScrollBar" TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="178"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="12" />
<RowDefinition Height="*"/>
<RowDefinition Height="12" />
</Grid.RowDefinitions>
<Border Grid.Row="1" CornerRadius="2" BorderThickness="0" >
<Border.Background>
<ImageBrush ImageSource="/HyperVibe;component/Images/Grey_Slider_Background.png" />
</Border.Background>
</Border>
<RepeatButton Grid.Row="0" Command="ScrollBar.LineUpCommand" Content=" ^" />
<!--IsDirectionReversed set to true draws a ScrollBar with a
Track whose lowest value is at the bottom.
The default orientation of a ScrollBar is for the Track
values to decrease from top to bottom.-->
<Track Grid.Row="1" Name="PART_Track" IsDirectionReversed="true">
<Track.Thumb>
<Thumb BorderThickness="1" DataContext="{Binding}" >
<Thumb.OpacityMask>
<ImageBrush ImageSource="/HyperVibe;component/Images/Green%20Slider.png" />
</Thumb.OpacityMask>
<Thumb.Background>
<ImageBrush ImageSource="/HyperVibe;component/Images/Green%20Slider.png" />
</Thumb.Background>
</Thumb>
</Track.Thumb>
</Track>
<RepeatButton Grid.Row="2" Command="ScrollBar.LineDownCommand" Content=" v" />
</Grid>
</ControlTemplate>
any help will be greatly appreciated.
Best Regards,
~Anup
Try if you can load the above control template as a DynamicResource and do the following in C# code. If you load it as a static resource then you will not be able to edit the resource as it will be sealed.
ControlTemplate myCtrlTpl = (ControlTemplate) FindResource("MyScrollBar");
Trigger tgrIsMouseOver = new Trigger { Property = Thumb.IsMouseOverProperty, Value = true };
Trigger tgrIsMouseNotOver = new Trigger { Property = Thumb.IsMouseOverProperty, Value = false };
ImageBrush mImgBrhDefault = (Create what ever brush either imagebrush or solidcolorbrush)
ImageBrush mImgBrhHighlight = (Create what ever brush either imagebrush or solidcolorbrush)
tgrIsMouseOver.Setters.Add(new Setter(Thumb.BackgroundProperty, mImgBrhHighlight));
tgrIsMouseNotOver.Setters.Add(new Setter(Thumb.BackgroundProperty, mImgBrhDefault));

Silverlight: Applying datacontext to an element within a style

I have defined a style in app.xaml. This style contains several text TextBlocks which I would like to controle as I apply the style to an object, in this case a UserPin.
How can I access these TextBlocks runtime?
I get the style by:
Style = Application.Current.Resources["UserPin"] as Style;
The style looks like this:
<Style x:Name="UserPin" TargetType="RRML_UserControls:UserPin" >
<Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
<Setter Property="AnchorPoint" Value="0.5,0.5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RRML_UserControls:UserPin">
<Grid Height="71.969" Width="Auto">
<Grid.RenderTransform>
<ScaleTransform x:Name="PART_PinScale" />
</Grid.RenderTransform>
<Grid.RowDefinitions>
<RowDefinition Height="29"/>
<RowDefinition Height="16"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.247*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="0.753*"/>
</Grid.ColumnDefinitions>
<Image Height="Auto" Source="Resources/Users.png" x:Name="PART_imgUser" VerticalAlignment="Top" Stretch="Uniform" Margin="0,0,0,0" Grid.Column="1">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,0" Width="Auto" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Center" TextAlignment="Center" x:Name="txtBottom" Text="{Binding Mode=OneWay, Path=LocationName}">
<TextBlock.DataContext>
<RRML_RRMLServiceReference:Location LocationName="Initial Name"/>
</TextBlock.DataContext>
</TextBlock>
<TextBlock HorizontalAlignment="Right" Margin="0,0,0,0" VerticalAlignment="Center" Text="L" TextWrapping="Wrap"/>
<TextBlock Margin="0,0,0,0" Text="R" TextWrapping="Wrap" d:LayoutOverrides="Width, Height" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The TextBlock value I'm trying to set is txtBottom.
As you can see I have tried to apply a datacontext and a databinding to the field. This works, but all objects get the value "Initial Name" of course.
My questions are:
how can I apply my datacontext so txtBottom.Text changes, or
how can I change the value of the TextBlock named txtBottom without databinding?
in short can I access these fields or properties at all?
Runtime :)
So far I have found that Triggers may be used only in WPF.
I think of something like this:
var styledobject = new NiceObject();
styledobject.Style = Application.Current.Resources["UserPin"] as Style;
styledobject.DataContext = locationData;
Where locationData is my object containing data.
If anyone wonders; I am placing icons on a map and want to name them.
You should not explicitly apply DataContext on the TextBlock. DataContext is inherited by child FrameworkElements. You should try to set data context explicitly as little and as high up the Visual Tree as possible (for your own sanity's sake :-))
If this is a custom control, you can override on the OnApplyTemplate method and use the GetTemplateChild(string name) to retrieve references to named elements within your control.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
TextBlock txtBottom = GetTemplateChild("txtBottom") as TextBlock;
}
Externally, if you must, you can imperatively access that specific control at runtime using an extension method to traverse the Visual Tree to find it by name.
public static T FindChild<T>(this DependencyObject element, string name)
where T : FrameworkElement
{
//Code to find the control
}

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