I have a large canvas. In the casvas I'm adding textblock in code behind on drage event. Each time I add a textblock the Scrollviewer set/shows the first textbloc. Thus I have to manually scroll up to last text block. So I want to problematically set the Scrollviewer at last element of the canvas after a new textblock added. XAML:-
<ScrollViewer x:Name="sv" HorizontalScrollBarVisibility="Auto" Margin="6,6,-835,66">
<Canvas x:Name="canvas" Height="450" Width="12000" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
</Canvas>
</ScrollViewer>
Code behind C#:-
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
if (tb_conter > 24)
return;
TextBlock[] tb = new TextBlock[elemet];
for (int i = 0; i < elemet; i++)
tb[i] = new TextBlock();
currentPoint = e.GetPosition(this.ContentPanel);
double x = currentPoint.X - oldPoint.X;
if (x >= 100)
{
tb[tb_conter].SetValue(Canvas.LeftProperty, tb_canvasLeft);
tb[tb_conter].SetValue(Canvas.TopProperty, tb_canvasTop);
tb[tb_conter].Text = time_scale.ToString();
canvas.Children.Add(tb[tb_conter]);
time_scale++;
tb_conter++;
tb_canvasLeft += tb_canvasTop;
}
else
Debug.WriteLine(x.ToString());
}
You want the ScrollViewer.ScrollToVerticalOffset method.
Better still would be adding items to an ObservableCollection<string> in a view model and binding that to a ListBox on the page.
Related
I have added 10 images in a stackpanel horizontally which is inside a scrollviewer. When user swipe the page ,the scrollviewer stops at certain position, if the scroll stops in the middle of 2 images like the first image shown below i want to set the image with number 3 to be automatically scroll and fit with the left side of the screen like in the second image
for (int i = 0; i <= 9; i++)
{
Uri uri = new Uri("http://d1mu9ule1cy7bp.cloudfront.net//catalogues/47/pages/p_" + i + "/thump.jpg");
ImageSource img1 = new BitmapImage(uri);
Image rect = new Image { RenderTransform = new TranslateTransform() };
rect.Source = img1;
stack.Children.Add(rect);
}
XAML:
<Grid x:Name="LayoutRoot" Width="480" Background="Transparent" Margin="0,-33,0,0" Height="800">
<ScrollViewer HorizontalContentAlignment="Left" HorizontalAlignment="Left" Name="scroll" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<StackPanel Name="stack" Width="Auto" Orientation="Horizontal" HorizontalAlignment="Left" >
</StackPanel>
</ScrollViewer>
</Grid>
The first thing you need to do is detect which item is overlapping the side of the screen. To do this, iterate over each item within the StackPanel and determine their location relative to some other element that has a fixed location on screen.
To do this, I use the following extension method:
/// <summary>
/// Gets the relative position of the given UIElement to this.
/// </summary>
public static Point GetRelativePosition(this UIElement element, UIElement other)
{
return element.TransformToVisual(other)
.Transform(new Point(0, 0));
}
i.e. for each item call the following;
Point position = stackPanelItem.GetRelativePosition(someFixedElement);
Using the location of each item, you should be able to work out which one overlaps the screen.
You then need to calculate by how much you need to scroll in order to ensure that your item is fully visible, then use ScrollViewer.ScrollToVerticalOffset to scroll to that location.
This probably isn't the nicest solution and I am sure there is a better way to achieve this but you could use the following :-
XAML :-
<ListBox x:Name="MyListBox"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
C# :-
DispatcherTimer myTimer = new DispatcherTimer();
// Constructor
public MainPage()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
MyListBox.Items.Add(new Button()
{
Content = i.ToString(),
Width = 200,
Height = 100,
});
MyListBox.MouseMove += new MouseEventHandler(MyListBox_MouseMove);
}
myTimer.Interval = TimeSpan.FromSeconds(1);
myTimer.Tick += new EventHandler(myTimer_Tick);
}
private void myTimer_Tick(object sender, EventArgs e)
{
myTimer.Stop();
SnapFirstItem();
}
private void MyListBox_MouseMove(object sender, MouseEventArgs e)
{
myTimer.Stop();
myTimer.Start();
}
private void SnapFirstItem()
{
foreach (Button currentButton in MyListBox.Items)
{
bool visible = MyListBox.TestVisibility(currentButton, System.Windows.Controls.Orientation.Horizontal, true);
if (visible)
{
MyListBox.ScrollIntoView(currentButton);
break;
}
}
}
The TestVisibility extension method is from the following :-
http://blogs.msdn.com/b/ptorr/archive/2010/10/12/procrastination-ftw-lazylistbox-should-improve-your-scrolling-performance-and-responsiveness.aspx
I override listbox's ArrangeOverride method want to show itemSource like this:
(i collcation DependencyObject on PrepareContainerForItemOverride method)
1 2 3 4
5 6 7 8
.....
......100
but when i scroll the scrollbar the array change like this:
1
2
3
4
5
......
100
protected override Size ArrangeOverride(Size finalSize)
{
if (this._ItemsDictionary.Count <= 0)
{
return base.ArrangeOverride(finalSize);
}
base.ArrangeOverride(finalSize);
finalSize = this.MeasureOverride(_availableSize);
double xMemory = 0;
double yMemory = 0;
double maxBoderWidth = 0;
double maxHeight = 0;
foreach (FrameworkElement element in _ItemsDictionary.Values)
{
if (xMemory + element.DesiredSize.Width <= finalSize.Width)
{
element.Arrange(new Rect(xMemory, yMemory, element.DesiredSize.Width, element.DesiredSize.Height));
xMemory += element.DesiredSize.Width;
maxHeight = Math.Max(element.DesiredSize.Height, maxHeight);
}
else
{
yMemory += maxHeight;
maxBoderWidth = Math.Max(maxBoderWidth, xMemory);
xMemory = 0;
maxHeight = 0;
element.Arrange(new Rect(xMemory, yMemory, element.DesiredSize.Width, element.DesiredSize.Height));
xMemory += element.DesiredSize.Width;
maxHeight = Math.Max(element.DesiredSize.Height, maxHeight);
}
}
return finalSize;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
FrameworkElement fElement = element as FrameworkElement;
if (!_ItemsDictionary.ContainsKey(item))
{
_ItemsDictionary.Add(item, fElement);
}
}
I may be wrong. But it seems that your base class is ItemsPresenter (or something inherited from it like ListBox). It's not a good idia. Becouse EACH ItemsPresenter have it's own ItemsPanel! And Silverlight use this panel for layouting items. So ItemsPresenter can't layout it's own items directly, only trou ItemsPanel panel.
1) I recomend you to use WrapPanel (that is part of Silverlight SDK) so you have it for free, i think this is what you want. Just replace ListBox.ItemsPanel property with WrapPanel and you going to get result that you wanted
2) If you want create your own pannel you better create new class and inherit it from Panel
public class SomeNewPanel: Panel
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
//you can add here your custom measure logic
return base.MeasureOverride(availableSize);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
{
//you can add here your custom arrange logic
return base.ArrangeOverride(finalSize);
}
}
and then use it in ListBox like this.
<Page x:Class="SilverlightApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
<!--Don't forget to add namespace of your newly created panel-->
xmlns:local="clr-namespace:SilverlightApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox x:Name="listBox1" ItemsSource="{Binding SomeItemSource}">
<!--ItemPanel property set or get Panel that-->
<!--will be used for layouting items-->
<ListBox.ItemsPanel>
<!--Here you and your newly created panle-->
<local:SomeNewPanel/>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Page>
I am using scroll viewer in my app which shows images in a horizontal view. I need to use animation while scrolling like in this video how to acheive this
http://www.youtube.com/watch?v=XVHVBMeqL24
I have tried this
public Storyboard CreateAndApplyStoryboard(UIElement targetElement)
{
Storyboard sb = new Storyboard();
DoubleAnimation animation =
new DoubleAnimation { From = 0, To = 500, Duration = new Duration(TimeSpan.FromSeconds(1.0)) };
//ExponentialEase ese = new ExponentialEase();
//ese.EasingMode = EasingMode.EaseIn;
//animation.EasingFunction = ese;
Storyboard.SetTarget(animation, targetElement);
Storyboard.SetTargetProperty(animation,
new PropertyPath(ScrollViewer.HorizontalOffsetProperty));
sb.Children.Add(animation);
return sb;
}
I am just showing the example i need to scroll from horizontal offset 0 to 500
How to use the dependency property?
<scrollviewer name="scroll">
<StackPanel Name="stack" Width="5000" Orientation="Horizontal" HorizontalAlignment="Left" >
<StackPanel.RenderTransform>
<TranslateTransform x:Name="Trans2" X="0" Y="0" />
</StackPanel.RenderTransform>
</StackPanel>
private void button1_Click(object sender, RoutedEventArgs e)
{
Storyboard sb = CreateAndApplyStoryboard(scroll);
sb.Begin();
}
i am trying to put a end-user license agreement (EULA) into a WP7 silverlight textblock control. however, it keeps truncating my text. why is this happening? is there a limit on the text size or number of characters a WP7 silverlight textblock can hold?
below is an example of what i've done in terms of xaml (the rest of the xaml surrounding is the default that is auto-generated).
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ScrollViewer>
<TextBlock x:Name="tbMsg" TextWrapping="Wrap"/>
</ScrollViewer>
</Grid>
i've also tried using a TextBox, but now, i can't even scroll within the TextBox. i've explicitly set the VerticalScrollBarVisibility to Visible too, but i still can't scroll down the TextBox. in fact, i don't even see the vertical scroll bar. i don't know if this observation is because i'm still viewing the UI via the emulator.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox x:Name="tbMsg" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
</Grid>
No UIElement can be larger than 2048 pixels in either direction (height or width). Any content which would be displayed beyond this area isn't displayed. Space for where this content would be is reserved within the visual tree though.
The work around for this is to use multiple elements to display large amounts of text.
Update
I've written my own parsers for dynaically displaying content of this sort. Ideally though you won't be working with large blocks of text at runtime though. This can be further complicated when the text contains links (to other pages, web content or email launchers).
When wanting to display EULAs or any large piece of text, you won't want to make it easy for the user to read and navigate. Afterall you are including the text as you want the user to read it.
If you have the text at design time you should take the opportunity to ensure that it is laid out appropriately and using separate TextBlocks for different sections and styling headings and sub-headings appropriately can help you do this.
It could be several things - the height of your textblock may be being constrained by another control, a style you have applied to the text may be causing it...
Can you post your source?
I want to share with you this code. It creates a number of TextBlocks and add as children to Stackpanel. You can create a Stackpanel inside your Scroll viewer since Scrollvewers can only take a single child element directly.
//constructor
List<char> countStoryvar = new List<char>(); //to store the text Body
private string incStorybody;
private int textMax = 2000; //You can modify this.
//methods
private void PlaceData()
{
incStorybody = "Your Story Source";
int countStory = incStorybody.Count();
countStoryvar = incStorybody.ToList();
double countStoryd = double.Parse(countStory.ToString());
if (countStoryd < textMax)
{
TextBlock txtBlock = new TextBlock();
txtBlock.TextWrapping = TextWrapping.Wrap;
readStackPanel.Children.Add(txtBlock);
}
else
{
double countStoryd2 = countStoryd / textMax; //fetch divisions
countStoryd2 = Math.Round(countStoryd2, 5); //getting Real no
string sountstring = countStoryd2.ToString();
string[] split = sountstring.Split(new string[] { "." }, StringSplitOptions.None); //to remove decimal
int countStoryi = int.Parse(split[0]);
int remainder = countStory - (textMax * countStoryi);
int iterationtimestin = countStoryi + 1;
for (int z = 0; z < iterationtimestin; z++)
{
int zlast = countStoryi - 1;
int multiple = 0;
int multiple1 = 0;
int multiplecounter = 0;
multiple = textMax * z;
if (z == 0)
{
multiplecounter = textMax;
}
else
{
if (z == countStoryi)
{
multiplecounter = countStory;
}
else
{
multiple1 = z + 1;
multiplecounter = textMax * multiple1;
}
}
LoadStackPanel(multiple, multiplecounter);
}
}
}
private void LoadStackPanel(int starting, int ending)
{
TextBlock txtBlock = new TextBlock();
txtBlock.TextWrapping = TextWrapping.Wrap;
for (int zi = starting; zi < ending; zi++)
{
incStoryInput = incStoryInput + countStoryvar[zi];
}
txtBlock.Text = incStoryInput;
readStackPanel.Children.Add(txtBlock);
incStoryInput = string.Empty;
}
In my app I have several pages. When I click on the Windows "Back" Button everything goes back as expected.
However I have 2 pages that are causing me grief. Page "A" is doing some binding in the XAML:
<ListBox x:Name="lbPrograms" ItemsSource="{Binding Items}" SelectionChanged="lbPrograms_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="DataTemplateStackPanel" Orientation="Horizontal">
<Image x:Name="ItemImage" Source="/images/ArrowImg.png" Height="43" Width="43" VerticalAlignment="Top" Margin="10,0,20,0"/>
<StackPanel>
<TextBlock x:Name="ItemText" Text="{Binding programName}" Margin="-2,-13,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock x:Name="DetailsText" Text="{Binding createDate}" Margin="0,-6,0,3" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
<Image x:Name="ItemFavs" Source="/images/favs.png" Height="43" Width="43" VerticalAlignment="Top" Margin="10,0,20,0"/>
<Image x:Name="ItemDelete" Source="/images/delete.png" Height="43" Width="43" VerticalAlignment="Top" Margin="10,0,20,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The code behind for Page A is fairly simple:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Set the data context of the listbox control to the sample data
if (DataContext == null)
DataContext = App.ViewModel;
App.ViewModel.Refresh();
lbPrograms.ItemsSource = App.ViewModel.Items;
}
private void lbPrograms_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
NavigationService.Navigate(new Uri("/DisplayProgram.xaml?selectedItem=" + lbPrograms.SelectedIndex, UriKind.Relative));
}
private void BackBtn_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
Page B has no binding in the XAML as I am taking the data from the ModelView a drawing it out dynamically on the screen. Like so:
private int index;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
//prevents error
if (int.Parse(selectedIndex) == -1)
{
return;
}
if ((DataContext == null))
{
index = int.Parse(selectedIndex);
App.ViewModel.Refresh();
DataContext = App.ViewModel.Items[index].nValDictionary;
int i = 0;
foreach (KeyValuePair<string, string> kvp in (((System.Collections.Generic.Dictionary<string, string>)(DataContext))))
{
StackPanel sp = new StackPanel();
sp.Name = "sp" + i;
sp.Background = new SolidColorBrush(Colors.Black);
sp.Width = 460;
WrapPanel wp = new WrapPanel();
wp.Name = "test" + i;
wp.Width = 300;
wp.Height = 200;
TextBlock txt = new TextBlock();
txt.Text = kvp.Key.ToString();
txt.Foreground = new SolidColorBrush(Colors.White);
sp.Children.Add(txt);
int chkBoxesVal = 0;
if (kvp.Value == "")
{
chkBoxesVal = 0;
}else{
chkBoxesVal = Convert.ToInt32(kvp.Value.ToString());
}
int iCount = 0;
for (iCount = 0; iCount <= chkBoxesVal - 1; iCount++)
{
CheckBox chk = new CheckBox();
chk.Name = i.ToString();
chk.Width = 56;
chk.Height = 70;
chk.Content = "";
//chk.Background = new SolidColorBrush(Colors.Black);
//chk.BorderBrush = new SolidColorBrush(Colors.White);
chk.Style = (Style)Application.Current.Resources["checkBoxNG"];
wp.Children.Add(chk);
}
sp.Children.Add(wp);
lbItems.Items.Add(sp);
i += 1;
}
}
}
}
}
So when I'm going forward everything works fine, but when I hit the Windows "Back" button on Page B I get an error. I stepped through my code and when I hit the "Back" button it does go back to Page A, but then it is also going to Page B, which then throws the error and stops. So can anyone tell me why this behavior is happening? I would expect that it would go back to Page A and just stop there. Not to go back to Page B. is there something in my code that is causing it to reload Page B? Any resources that you can provide that might explain this behavior is also welcome!
Thanks!
It looks like SelectionChanged on Page A is firing as a result of the ItemsSource initialisation you're doing in OnNavigatedTo.
You could verify the SelectedIndex is -1 before taking any action in the SelectionChanged event.
Alternatively you could remove any existing event handler on SelectionChanged while doing this initialisation and restore that event handler on completion.
My assumption is that lbPrograms_SelectionChanged event occurs when you press back button and page A is loaded again.
Change your navigation design. For your DataTemplateStackPanel you could use ManipulationStarted event
and inside add
NavigationService.Navigate(new Uri("/DisplayProgram.xaml?selectedItem=" + lbPrograms.SelectedIndex, UriKind.Relative));