ScrollViewer Control in Wpf - wpf

<ScrollViewer VerticalScrollBarVisibility="Auto" >
<TextBlock Text="{Binding Status}"
HorizontalAlignment="Center" />
</ScrollViewer>
The Status always I'm adding to it, that is to say that in the code behind always I do this thing:
Status+= Environment.NewLine + "Hi";
And the ScrollViewer increasing, but the Status that I see is the first one, when I want to see the last one I need to scroll underneath to see it, my question is: how I can made the Scrollviewer scrolling underneath automaticaly?, that meaning I want always see the last status not the first.
Sorry about the broken english.

Name your ScrollViewer so you can access it in the code behind, like this:
<ScrollViewer x:Name="MyScrollViewer" VerticalScrollBarVisibility="Auto" >
<TextBlock Text="{Binding Status}"
HorizontalAlignment="Center" />
</ScrollViewer>
Then you can do this:
Status+= Environment.NewLine + "Hi";
MyScrollViewer.ScrollToEnd();
With the way I do MVVM, I have access to my ViewModel from my View, so when the View first loads, I'd subscribe to the PropertyChanged event on my ViewModel as so:
MyViewModel.PropertyChanged += ViewModelChanged;
and then in the ViewModelChanged callback I'd have this:
private void ViewModelChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Status")
MyScrollViewer.ScrollToEnd();
}
Every time the ViewModel's Status property changes, the ScrollViewer will now scroll to end. Just remember to unsubscribe from MyViewModel.PropertyChanged when you leave that screen to avoid a memory leak.

Related

When scrollviewer becomes visible other controls move to different position

I’ve got a listbox which has vertical scrollviewer set to auto, so when user adds more items than can be visible on the screen, then the scrollbar appears. The problem is that when it appears, it moves other things, like the add button which is placed next to listbox. Is there any way to have it hidden (so it has the space for it), but then make it visible when needed?
I just don’t want all that stuff around to jump, anytime scrollviewer becomes visible or hidden.
Regards
Daniel
This happens when you donot set the width of your ListBox or you set it to Auto(which is by default). Try setting it to some value like below code, you shouldn't face any issue.
<StackPanel Orientation="Horizontal" OverridesDefaultStyle="True">
<ListBox Height="150" Width="125" x:Name="NamesListBox"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Button Height="30" Width="100"
Click="ButtonBase_OnClick" Content="Add"/>
</StackPanel>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
NamesListBox.Items.Add(DateTime.Now);
}

StringUpDown in WPF?

I have a question regarding the following question about NumericUpDown:
Good NumericUpDown equivalent in WPF?
How I can do this for 12 months of a year? I am planning to use a vertical ScrollBar with a textbox. I want to link the vertical ScrollBar up and down clicks to increment and decrement the months in the textbox using C#.
<ScrollBar x:Name="scbm"
HorizontalAlignment="Left" Height="26" Margin="230,195,0,0"
VerticalAlignment="Top" RenderTransformOrigin="0.542,0.83"/>
<TextBox x:Name="txtm"
HorizontalAlignment="Left" Height="23" Margin="139,195,0,0"
TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="66"/>
Can anyone tell me how I can accomplish this?
You can have a look This article that does something similar with a datepicker.
Basically you'll want to have a property for the month, and depending on your approach (code behind / mvvm), handle the click events on the up/down buttons, or the keyboard keydown event to handle them logic (so in your case, add the up/down buttons, and wire them to the events, naming them appropriately).
For example, having this on your xaml:
<DatePicker ... PreviewKeyDown="PreviewKeyDown_EventHandler" ... />
And something like this in your code behind:
private void PreviewKeyDown_EventHandler(object sender, System.Windows.Input.KeyEventArgs e)
{
// Avoid them nasty exceptions is the user hits "up" or "down" with no date selected:
if (sender == null || ((DatePicker)sender).SelectedDate == null)
return;
// Do this on up
if (e.Key == Key.Up)
{
((DatePicker)sender).SelectedDate =
((DatePicker)sender).SelectedDate.GetValueOrDefault().AddMonths(1);
}
// And this on down
if (e.Key == Key.Down)
{
((DatePicker)sender).SelectedDate =
((DatePicker)sender).SelectedDate.GetValueOrDefault().AddMonths(-1);
}
}
That example uses a datetime as the property, but you can do similar things with an int if that's what you're after.
(again, have a look at the link for more options, examples, and code example if you'd like)
Edit:
This doesn't have theh benefits of the up/down keys like the article's example, but this works:
<ScrollBar x:Name="scbm"
SmallChange="1" Maximum="12" Minimum="1"
Value="{Binding MonthScrollBar}"
HorizontalAlignment="Left" Height="26" Margin="230,195,0,0"
VerticalAlignment="Top" RenderTransformOrigin="0.542,0.83" />
<TextBox x:Name="txtm"
Text="{Binding MonthScrollBar}"
HorizontalAlignment="Left" Height="23" Margin="139,195,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="66" />
On my ViewModel (or your codebehind, if you want, do something similar)
public int MonthScrollBar
{
get { return _monthScrollBar; }
set { _monthScrollBar = value;
RaisePropertyChanged("MonthScrollBar");}
}
private int _monthScrollBar;
And setting the property value to whatever you want on the constructor.
Note that I'm using MVVM, binding to a property with notification change (so changes are propagating to the View), and initializing it.
Both the ScrollBar and TextBox bind to the same MonthScrollBar property)
If you're doing code behind, you can access it directly from the code behind.

WPF Expander, get the Header name out of a child element

I have a WPF Expander, such as this:
<Expander Canvas.Left="251" Canvas.Top="425" Header="expander1" Height="100" Name="expander1">
<Grid>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Option 1" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 2" Checked="chk_DoThis" />
<CheckBox Margin="4" Content="Option 3" Checked="chk_DoThis" />
</StackPanel>
</Grid>
</Expander>
When a checkbox is clicked, I fire off a 'Checked' event.
Is there some way to pull out a string that contains the 'Header' of the Expander? In this example, I want to pull out 'expander1' and assign that to a string.
I tried a few ways of doing this and couldn't get it to work. I have done this same concept using TreeViewItems and using a Header.Parent.ToString() to get what I wanted. No luck here. This is what I'm referring to:
string child = ((TreeViewItem)((TreeViewItem)((TreeView)sender).SelectedItem)).Header.ToString();
Does anyone know of a way I could do this for my Expander example. Googling and searching this site has yielded no return. It's probably something easy and I'm just overlooking it.
Thanks to anyone that has some ideas.
You can easily get the Expander from the CheckBox. Just iterate to the VisualTree and get the Top most parent's Expander. I just simply did only one parent in the below example.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Expander expander = (Expander)checkBox.Parent;
if (expander != null)
{
string str = expander.Header.ToString();
Debug.WriteLine(str);
}
}
I hope it will help you.

How do I configure a TextBox control to automatically resize itself vertically when text no longer fits on one line?

How do I configure a TextBox control to automatically resize itself vertically when text no longer fits on one line?
For example, in the following XAML:
<DockPanel LastChildFill="True" Margin="0,0,0,0">
<Border Name="dataGridHeader"
DataContext="{Binding Descriptor.Filter}"
DockPanel.Dock="Top"
BorderThickness="1"
Style="{StaticResource ChamelionBorder}">
<Border
Padding="5"
BorderThickness="1,1,0,0"
BorderBrush="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleBorder}}}">
<StackPanel Orientation="Horizontal">
<TextBlock
Name="DataGridTitle"
FontSize="14"
FontWeight="Bold"
Foreground="{DynamicResource {ComponentResourceKey
TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}"/>
<StackPanel Margin="5,0" Orientation="Horizontal"
Visibility="{Binding IsFilterEnabled, FallbackValue=Collapsed, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"
IsEnabled="{Binding IsFilterEnabled, FallbackValue=false}" >
<TextBlock />
<TextBox
Name="VerticallyExpandMe"
Padding="0, 0, 0, 0"
Margin="10,2,10,-1"
AcceptsReturn="True"
VerticalAlignment="Center"
Text="{Binding QueryString}"
Foreground="{DynamicResource {ComponentResourceKey
TypeInTargetAssembly=dc:NavigationPane,
ResourceId={x:Static dc:NavigationPaneColors.NavPaneTitleForeground}}}">
</TextBox>
</StackPanel>
</StackPanel>
</Border>
</Border>
</DockPanel>
The TextBox control named "VerticallyExpandMe" needs to automatically expand vertically when the text bound to it does not fit on one line. With AcceptsReturn set to true, TextBox expands vertically if I press enter within it, but I want it do do this automatically.
Although Andre Luus's suggestion is basically correct, it won't actually work here, because your layout will defeat text wrapping. I'll explain why.
Fundamentally, the problem is this: text wrapping only does anything when an element's width is constrained, but your TextBox has unconstrained width because it's a descendant of a horizontal StackPanel. (Well, two horizontal stack panels. Possibly more, depending on the context from which you took your example.) Since the width is unconstrained, the TextBox has no idea when it is supposed to start wrapping, and so it will never wrap, even if you enable wrapping. You need to do two things: constrain its width and enable wrapping.
Here's a more detailed explanation.
Your example contains a lot of detail irrelevant to the problem. Here's a version I've trimmed down somewhat to make it easier to explain what's wrong:
<StackPanel Orientation="Horizontal">
<TextBlock Name="DataGridTitle" />
<StackPanel
Margin="5,0"
Orientation="Horizontal"
>
<TextBlock />
<TextBox
Name="VerticallyExpandMe"
Margin="10,2,10,-1"
AcceptsReturn="True"
VerticalAlignment="Center"
Text="{Binding QueryString}"
>
</TextBox>
</StackPanel>
</StackPanel>
So I've removed your containing DockPanel and the two nested Border elements inside of that, because they're neither part of the problem nor relevant to the solution. So I'm starting at the pair of nested StackPanel elements in your example. And I've also removed most of the attributes because most of them are also not relevant to the layout.
This looks a bit weird - having two nested horizontal stack panels like this looks redundant, but it does actually make sense in your original if you need to make the nested one visible or invisible at runtime. But it makes it easier to see the problem.
(The empty TextBlock tag is also weird, but that's exactly as it appears in your original. That doesn't appear to be doing anything useful.)
And here's the problem: your TextBox is inside some horizontal StackPanel elements, meaning its width is unconstrained - you have inadvertently told the text box that it is free to grow to any width, regardless of how much space is actually available.
A StackPanel will always perform layout that is unconstrained in the direction of stacking. So when it comes to lay out that TextBox, it'll pass in a horizontal size of double.PositiveInfinity to the TextBox. So the TextBox will always think it has more space than it needs. Moreover, when a child of a StackPanel asks for more space than is actually available, the StackPanel lies, and pretends to give it that much space, but then crops it.
(This is the price you pay for the extreme simplicity of StackPanel - it's simple to the point of being bone-headed, because it will happily construct layouts that don't actually fit. You should only use StackPanel if either you really do have unlimited space because you're inside a ScrollViewer, or you are certain that you have sufficiently few items that you're not going to run out of space, or if you don't care about items running off the end of the panel when they get too large and you don't want the layout system to try to do anything more clever than simply cropping the content.)
So turning on text wrapping won't help here, because the StackPanel will always pretend that there's more than enough space for the text.
You need a different layout structure. Stack panels are the wrong thing to use because they will not impose the layout constraint you need to get text wrapping to kick in.
Here's a simple example that does roughly what you want:
<Grid VerticalAlignment="Top">
<DockPanel>
<TextBlock
x:Name="DataGridTitle"
VerticalAlignment="Top"
DockPanel.Dock="Left"
/>
<TextBox
Name="VerticallyExpandMe"
AcceptsReturn="True"
TextWrapping="Wrap"
Text="{Binding QueryString}"
>
</TextBox>
</DockPanel>
</Grid>
If you create a brand new WPF application and paste that in as the content of the main window, you should find it does what you want - the TextBox starts out one line tall, fills the available width, and if you type text in, it'll grow one line at a time as you add more text.
Of course, layout behaviour is always sensitive to context, so it may not be enough to just throw that into the middle of your existing application. That will work if pasted into a fixed-size space (e.g. as the body of a window), but will not work correctly if you paste it into a context where width is unconstrained. (E.g., inside a ScrollViewer, or inside a horizontal StackPanel.)
So if this doesn't work for you, it'll be because of other things wrong elsewhere in your layout - possibly yet more StackPanel elements elsewhere. From the look of your example, it's probably worth spending some time thinking about what you really need in your layout and simplifying it - the presence of negative margins, and elements that don't appear to do anything like that empty TextBlock are usually indicative of an over-complicated layout. And unnecessary complexity in a layout makes it much hard to achieve the effects you're looking for.
Alternatively, you could constrain your TextBlock's Width by binding it to a parent's ActualWidth, for example:
<TextBlock Width="{Binding ElementName=*ParentElement*, Path=ActualWidth}"
Height="Auto" />
This will force it to resize its height automatically too.
Use MaxWidth and TextWrapping="WrapWithOverflow".
I'm using another simple approach that allows me not to change the document layout.
The main idea is not to set the control Width before it starts changing. For TextBoxes, I handle the SizeChanged event:
<TextBox TextWrapping="Wrap" SizeChanged="TextBox_SizeChanged" />
private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e)
{
FrameworkElement box = (FrameworkElement)sender;
if (e.PreviousSize.Width == 0 || box.Width < e.PreviousSize.Width)
return;
box.Width = e.PreviousSize.Width;
}
You can use this class which extends TextBlock. It does auto-shrinking and takes MaxHeight / MaxWidth into consideration:
public class TextBlockAutoShrink : TextBlock
{
private double _defaultMargin = 6;
private Typeface _typeface;
static TextBlockAutoShrink()
{
TextBlock.TextProperty.OverrideMetadata(typeof(TextBlockAutoShrink), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
}
public TextBlockAutoShrink() : base()
{
_typeface = new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, this.FontStretch, this.FontFamily);
base.DataContextChanged += new DependencyPropertyChangedEventHandler(TextBlockAutoShrink_DataContextChanged);
}
private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var t = sender as TextBlockAutoShrink;
if (t != null)
{
t.FitSize();
}
}
void TextBlockAutoShrink_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FitSize();
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
FitSize();
base.OnRenderSizeChanged(sizeInfo);
}
private void FitSize()
{
FrameworkElement parent = this.Parent as FrameworkElement;
if (parent != null)
{
var targetWidthSize = this.FontSize;
var targetHeightSize = this.FontSize;
var maxWidth = double.IsInfinity(this.MaxWidth) ? parent.ActualWidth : this.MaxWidth;
var maxHeight = double.IsInfinity(this.MaxHeight) ? parent.ActualHeight : this.MaxHeight;
if (this.ActualWidth > maxWidth)
{
targetWidthSize = (double)(this.FontSize * (maxWidth / (this.ActualWidth + _defaultMargin)));
}
if (this.ActualHeight > maxHeight)
{
var ratio = maxHeight / (this.ActualHeight);
// Normalize due to Height miscalculation. We do it step by step repeatedly until the requested height is reached. Once the fontsize is changed, this event is re-raised
// And the ActualHeight is lowered a bit more until it doesnt enter the enclosing If block.
ratio = (1 - ratio > 0.04) ? Math.Sqrt(ratio) : ratio;
targetHeightSize = (double)(this.FontSize * ratio);
}
this.FontSize = Math.Min(targetWidthSize, targetHeightSize);
}
}
}

Setting focus on a ListBox item breaks keyboard navigation

After selecting ListBox item programmatically it is needed to press down\up key two times to move the selection. Any suggestions?
View:
<ListBox Name="lbActions" Canvas.Left="10" Canvas.Top="10"
Width="260" Height="180">
<ListBoxItem Name="Open" IsSelected="true" Content="Open"></ListBoxItem>
<ListBoxItem Name="Enter" Content="Enter"></ListBoxItem>
<ListBoxItem Name="Print" Content="Print"></ListBoxItem>
</ListBox>
Code:
public View()
{
lbActions.Focus();
lbActions.SelectedIndex = 0; //not helps
((ListBoxItem) lbActions.SelectedItem).Focus(); //not helps either
}
Don't set the focus to the ListBox... set the focus to the selected ListBoxItem. This will solve the "two keyboard strokes required" problem:
if (lbActions.SelectedItem != null)
((ListBoxItem)lbActions.SelectedItem).Focus();
else
lbActions.Focus();
If your ListBox contains something else than ListBoxItems, you can use lbActions.ItemContainerGenerator.ContainerFromIndex(lbActions.SelectedIndex) to get the automatically generated ListBoxItem.
If you want this to happen during window initialization, you need to put the code in the Loaded event rather than into the constructor. Example (XAML):
<Window ... Loaded="Window_Loaded">
...
</Window>
Code (based on the example in your question):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lbActions.Focus();
lbActions.SelectedIndex = 0;
((ListBoxItem)lbActions.SelectedItem).Focus();
}
You can do this easily in XAML too. Please note that this will set logical focus only.
For example:
<Grid FocusManager.FocusedElement="{Binding ElementName=itemlist, Path=SelectedItem}">
<ListBox x:Name="itemlist" SelectedIndex="1">
<ListBox.Items>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
<ListBoxItem>Three</ListBoxItem>
<ListBoxItem>Four</ListBoxItem>
<ListBoxItem>Five</ListBoxItem>
<ListBoxItem>Six</ListBoxItem>
</ListBox.Items>
</ListBox>
</Grid>
Seems that there are two levels of Focus for ListBox control: ListBox itself and ListBoxItem. Like Heinzi said, directly set Focus for the ListBoxItem will avoid the case that you have to click twice on direction key in order to go through all ListBoxItems.
I found out this after several hours work, now it works perfect on my APP.

Resources