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>
Related
I have placed a UserControl inside a ListView.
How do I get the control of this UserControl in the view.
If I place it inside a ListView, I am unable to access it in the view. I also do not wish to make any changes to the listView binding source.
Its name isn't accessible directly in the view.
I am able to access the events but not Properties(x:Name , Visibility etc..).
You can use VisualTreeHelper class to get your UserControl .
Get each ListViewItem by calling the ListView's ContainerFromItem or ContainerFromIndex.
Create a recursive function to find the DependencyObjects that are in each ListViewItem as a UserControl.
I made a simple to show how it works. You can refer to the following code.
MainPage.xaml
<Grid>
<ListView x:Name="MyListView" Margin="0,0,0,109">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Grid>
<local:MyUserControl1></local:MyUserControl1>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Button" Margin="682,943,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
MainPage.cs
public List<string> ItemsSourceList { get; set; }
public MainPage()
{
this.InitializeComponent();
ItemsSourceList = new List<string>();
ItemsSourceList.Add("1");
ItemsSourceList.Add("2");
ItemsSourceList.Add("3");
ItemsSourceList.Add("4");
ItemsSourceList.Add("5");
MyListView.ItemsSource = ItemsSourceList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var strItem in ItemsSourceList)
{
// get every listview item first
ListViewItem item = MyListView.ContainerFromItem(strItem) as ListViewItem;
// the DependencyObject is the UserControl that you want to get
DependencyObject myUserControl = FindChild(item);
}
}
public DependencyObject FindChild(DependencyObject parant)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is MyUserControl1)
{
//Here can get the MyUserControl1.
MyUserControl1 myUserControl = (MyUserControl1)MyChild;
myUserControl.Foreground = new SolidColorBrush(Colors.Red);
return myUserControl;
}
else
{
var res = FindChild(MyChild);
return res;
}
}
return null;
}
In this case isn't supposed that I can see the values from list in ListPicker?
xaml
<toolkit:ListPicker
x:Name="lpkBoards"
ItemsSource="{Binding AllBoards}"
DisplayMemberPath="Name" >
</toolkit:ListPicker>
xaml.cs
public SettingsPage()
{
InitializeComponent();
// Set the page DataContext property to the ViewModel.
this.DataContext = App.ViewModel;
...
boardsTask.ContinueWith(
(call) =>
{
App.ViewModel.AllBoards = call.Result.ToList();
}
);
ViewModel
// All to-do items.
private List<Board> _allBoards;
public List<Board> AllBoards
{
get { return _allBoards; }
set
{
_allBoards = value;
NotifyPropertyChanged("AllBoards");
}
}
You need to change the List<Board> to ObservalbeCollection<Board> if you are trying to bind it to a UI element and want it to work.
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 have a a Pivot that has a ListBox defined as its Pivot.ItemTemplate as the following.
<controls:Pivot x:Name="pivot">
<controls:Pivot.ItemTemplate>
<DataTemplate>
<ListBox x:Name="listBox">
...
</ListBox>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
how do i programmatically access the corresponding ListBox control corresponding to the Pivot.SelectedItem or Pivot.SelectedIndex?
i tried something similar to this link http://www.windowsphonegeek.com/tips/how-to-access-a-control-placed-inside-listbox-itemtemplate-in-wp7.
var count = VisualTreeHelper.GetChildrenCount(pivotItem);
for(int i=0; i < count; i++) {
var child = VisualTreeHelper.GetChild(pivotItem, i);
if(child is ListBox) {
//do something
} else {
Debug.WriteLine(child.GetType());
}
}
for some reason, i get System.Windows.Controls.Grid on the Debug.WriteLine.
the reason why i need to get a handle or access the ListBox inside the Pivot (that is currently on display/selected), is because i need to reset its view (scroll it back to the top). the ListBox is data bound to ObservableCollection, and when i update the collection, the scroll position needs to be placed back to the top; otherwise, everything works (data binding/visual display), except now the view is stuck in the middle or where the user currently is. if there's an easier way to do this without getting a handle on the ListBox, i'm open to that solution as well.
just in case anyone is interested, i tinkered and came up with something that works specifically for my case. the code is below. basically, i had to get the PivotItem first.
PivotItem pivotItem = pivot.ItemContainerGenerator.ContainerFromItem(myObject) as PivotItem;
i then created a local variable to store the ListBox (if it's found) and recursed the tree view model.
ListBox listBox = null;
Recurse(pivotItem, ref listBox);
and my Recurse function looks like the following.
private void Recurse(DependencyObject obj, ref ListBox listBox) {
if(obj is ListBox) {
listBox = obj as ListBox;
return;
}
var count = VisualTreeHelper.GetChildrenCount(obj);
for(int i=0; i < count; i++) {
var child = VisualTreeHelper.GetChild(obj, i);
Recurse(child, ref listBox);
}
}
try:
(Listbox)VisualTreeHelper.GetChild((pivot.SelectedItem as PivotItem), 0);
Looks like this was a while back, but this is what worked for me:
First get the PivotItem:
PivotItem pivotItem = Pivot.ItemContainerGenerator.ContainerFromItem(Pivot.SelectedItem) as PivotItem;
Then get the first child, a ListBox, from the PivotItem:
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject {
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++) {
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T) {
return (T)child;
} else {
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
Then call:
ListBox listBox = FindFirstElementInVisualTree<ListBox>(pivotItem);
use StackPanel inside your ListBox
this link may help you
http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo-1-virtualizingstackpanel-vs-stackpanel-as-a-listbox-itemspanel.aspx
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.