I am facing the problem that I cannot open an anchorable of type X after I have loaded my old layout. This happens only when I have closed the anchorable of type X before saving the layout.
Does anyone have a similar problem with AvalonDock? Is this a bug of AvalonDock? After years of debugging, I fear that the binding <Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/> doesn't get updated correctly in the view, when changing IsActive in the ViewModel. AvalonDock should be responsible for this task. But maybe the problem is the loading and saving of the layout?
The code
View
I am loading the saved layout of my anchorables (= tool windows) in the Loaded Event of my DockingManager in my View like this (simplified):
string savedLayout = Properties.Settings.Default.Layout;
XmlDocument doc = new XmlDocument();
doc.LoadXml(savedLayout);
// very simplified code. load saved xml layout and add anchorables to the dockmanager
doc.SelectNodes("//LayoutAnchorable").OfType<XmlNode>().ToList().ForEach(anchorable =>
{
this.DockManager.AnchorablesSource.Add(anchorable);
});
I am saving the current layout of my anchorables in the Closing Event of my MainWindow in my View like this (simplified):
XmlDocument doc = new XmlDocument();
XmlLayoutSerializer xmlLayoutSerializer = new XmlLayoutSerializer(this.DockManager);
using (MemoryStream stream = new MemoryStream())
{
xmlLayoutSerializer.Serialize(stream);
stream.Seek(0, SeekOrigin.Begin);
doc.Load(stream);
}
// here happens some magic. i think this code is not responsible for my problem
Properties.Settings.Default.Layout = doc.OuterXml;
The ViewModel is bound to the ViewModel in the XAML like this (simplified):
<xcad:DockingManager x:Name="DockManager" AnchorablesSource="{Binding Tools}" Loaded="DockManager_Loaded">
<xcad:DockingManager.LayoutItemContainerStyle>
<Style TargetType="{x:Type dockctrl:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.ContentId}" />
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}" />
<Setter Property="CanClose" Value="{Binding Model.CanClose, Mode=TwoWay}" />
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource Bool2vis}, ConverterParameter={x:Static Visibility.Hidden}}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}" />
<Setter Property="IconSource" Value="{Binding Model.IconSource}" />
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}" />
</Style>
</xcad:DockingManager.LayoutItemContainerStyle>
[...]
ViewModel
The anchorable is opened in the ViewModel of the MainWindow. Here is the example code for the messages:
public ObservableCollection<ToolBoxViewModelBase> Tools { get; } = new ObservableCollection<ToolBoxViewModelBase>();
public MainWindowViewModel()
{
// [...]
this.MessagesWindow = new MessagesWindowViewModel();
SimpleIoc.Default.Register<MessagesWindowViewModel>(() => this.MessagesWindow);
this.ShowMessagesWindowCommand = new RelayCommand(() => this.OpenToolBox(this.MessagesWindow));
// [...]
}
public void OpenToolBox<T>(T viewModel) where T : ToolBoxViewModelBase
{
// [...]
viewModel.IsVisible = true;
viewModel.IsActive = true;
// [...]
}
Just let me know if you need more information or wether i have missed to add some code!
Maybe I misunderstood you question but... IsActive property is not used for opening a tool saved into the layout. That property is used to set a Tool as active (focused). In order to open the tool saved into the layout you should handle the layoutSerializer_LayoutSerializationCallback attached the
Something like this:
var layoutSerializer = new XmlLayoutSerializer(this.DockManager);
layoutSerializer.LayoutSerializationCallback += layoutSerializer_LayoutSerializationCallback;
protected virtual void layoutSerializer_LayoutSerializationCallback(object sender, LayoutSerializationCallbackEventArgs e)
{
try
{
var model = this.Docs.Union(this.Tools).FirstOrDefault(vm => vm.ContentId == e.Model.ContentId);
if (model != null)
{
e.Content = model;
}
else
{
// Log load layout error info
}
}
catch (Exception ex)
{
// Log load layout error info
}
}
Related
I've been set to maintain a wpf application where there is a listbox for logging purposes.
The items displayed using listbox are of type TextMessage, i.e. the listbox is bound to these text messages via
ObservableCollection<TextMessage> Messages;
listBox.DataContext = Messages;
Messages are then added with something like
Messages.Add(new TextMessage("Test", TypeOfMessage.Headline));
This is the definition of the class TextMessage
public enum TypeOfMessage
{
Normal,
Headline,
Focus,
Important,
Fail,
Success
}
public class TextMessage
{
public TextMessage(string content, TypeOfMessage typeOfMessage)
{
Content = content;
TypeOfMessage = typeOfMessage;
CreationTime = DateTime.Now;
}
public string Content { get; }
public TypeOfMessage TypeOfMessage { get; }
public DateTime CreationTime { get; }
}
The xaml definition for the listbox is something like this:
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="196" Margin="101,77,0,0" VerticalAlignment="Top" Width="256" ItemsSource="{Binding}" SelectionMode="Multiple">
<ListBox.InputBindings>
<KeyBinding
Key="C"
Modifiers="Control"
Command="Copy"
/>
</ListBox.InputBindings>
<ListBox.CommandBindings>
<CommandBinding
Command="Copy"
Executed="DoPerformCopy"
/>
</ListBox.CommandBindings>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="TextToShow" Text="{Binding Content}"></TextBlock>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Normal">
<Setter TargetName="TextToShow" Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Focus">
<Setter TargetName="TextToShow" Property="Foreground" Value="Black"/>
<Setter TargetName="TextToShow" Property="FontWeight" Value="Bold"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Headline">
<Setter TargetName="TextToShow" Property="Foreground" Value="RoyalBlue"/>
<Setter TargetName="TextToShow" Property="FontWeight" Value="Bold"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Important">
<Setter TargetName="TextToShow" Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Fail">
<Setter TargetName="TextToShow" Property="Foreground" Value="Red"/>
<Setter TargetName="TextToShow" Property="FontWeight" Value="Bold"/>
</DataTrigger>
<DataTrigger Binding="{Binding TypeOfMessage}" Value="Success">
<Setter TargetName="TextToShow" Property="Foreground" Value="Green"/>
<Setter TargetName="TextToShow" Property="FontWeight" Value="Bold"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works nicely (i.e messages are displayed in the listbox in different font weight and color depending on their type), but now for the question :
Is there any way using BindingExpression or any other means to get the font formatting and coloring from code behind from the xaml definitions ?
The reason is that I want to just have the formatting in one place (just in the xaml as it is right now) but still be able to reuse it when I want to copy the contents (using code behind) including font formatting to the clipboard.
Example:
private void DoPerformCopy()
{
RichTextBox rtb = new RichTextBox();
foreach (TextMessage message in (listBox as ListBox)?.SelectedItems.Cast<TextMessage>().ToList())
{
TextPointer startPos = rtb.CaretPosition;
rtb.AppendText(message.Content);
rtb.Selection.Select(startPos, rtb.CaretPosition.DocumentEnd);
//
// Here it would be very nice to instead having multiple switch statements to get the formatting for the
// TypeOfMessage from the xaml file.
SolidColorBrush scb = new SolidColorBrush(message.TypeOfMessage == TypeOfMessage.Fail ? Colors.Red);
//
rtb.Selection.ApplyPropertyValue(RichTextBox.ForegroundProperty, scb);
}
// Now copy the whole thing to the Clipboard
rtb.Selection.Select(rtb.Document.ContentStart, rtb.Document.ContentEnd);
rtb.Copy();
}
Since I'm new to wpf, I'd really appreciate if someone has a tip for solving this. (I've tried hard to find an solution here at stackoverflow, but so far I've been unsuccessful)
Thanks in advance,
King regards
Magnus
Make a ContentPresenter with Content set to your TextMessage. Set the ContentTemplate to listBox.ItemTemplate and apply the template. It will create the visuals (TextBlock in this case). Then, just parse off the values from the TextBlock.
Also, your RichTextBox selection code wasn't working quite right so I fixed that by just inserting TextRanges to the end of it instead of trying to get the selection right.
private void DoPerformCopy(object sender, EventArgs e)
{
RichTextBox rtb = new RichTextBox();
foreach (TextMessage message in (listBox as ListBox)?.SelectedItems.Cast<TextMessage>().ToList())
{
ContentPresenter cp = new ContentPresenter();
cp.Content = message;
cp.ContentTemplate = listBox.ItemTemplate;
cp.ApplyTemplate();
var tb = VisualTreeHelper.GetChild(cp, 0) as TextBlock;
var fg = tb.Foreground;
var fw = tb.FontWeight;
var tr = new TextRange(rtb.Document.ContentEnd, rtb.Document.ContentEnd);
tr.Text = message.Content;
tr.ApplyPropertyValue(RichTextBox.ForegroundProperty, fg);
tr.ApplyPropertyValue(RichTextBox.FontWeightProperty, fw);
}
// Now copy the whole thing to the Clipboard
rtb.Selection.Select(rtb.Document.ContentStart, rtb.Document.ContentEnd);
rtb.Copy();
}
In my WPF application, using MVVM Light, I use a DataGrid to display datas from a specific object collection.
As requirement I had seperated the headers in a first list, and the complete object collection in another one.
After reading lots of tutorials, posts, blogs... it appeared that ErrorDataInfo has to be placed in the ViewModel to manage the model properties error.
Here, as a try, I want to display a very basic error as you'll see in the code.
The app process is that the user choose a csv file, this file is converted to my object collection and then displayed in the DataGrid. The cells on error state should be displayed at this time (and the error message in a ToolTip).
I've tried many things but my DataGrid is still not displaying cells in errors.
Here is my ViewModel code :
public class ImportViewModel : ViewModelBase, IDataErrorInfo
{
#region Validation
public string Error
{
get
{
return string.Empty;
}
}
public string this[string columnName]
{
get
{
string result = null;
if (null != ListOfObjects)
{
if (_imp.ListOfHeaders.Where(x => x.Name == "EAN Code")
.ToString() == columnName)
if (ListOfObjects.Select(x => x.ean_cod).Single().Length > 3)
result = "Code must not be more than 3 digits";
}
return result;
}
}
And my View xaml code
<DataGrid Grid.Row="1"
AutoGenerateColumns="True"
ItemsSource="{Binding ListOfObjects,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
Thx for your help.
I use AvalonDock with MVVM in a WPF project.
When I hit the "X" (Close button of the tab) my document closes but stays in memory. It seems that it is only hidden. It is not removed from my Model.Documents collection.
If I add DockingManager_DocumentClosing and try to remove my document from the collection, I receive an Exception in the following method of Xceed.Wpf.AvalonDock.Layout.LayoutContent because parentAsContainer is null.
/// <summary>
/// Close the content
/// </summary>
/// <remarks>Please note that usually the anchorable is only hidden (not closed). By default when user click the X button it only hides the content.</remarks>
public void Close()
{
var root = Root;
var parentAsContainer = Parent as ILayoutContainer;
parentAsContainer.RemoveChild(this);
if (root != null)
root.CollectGarbage();
OnClosed();
}
Does anybody know how I could manage document in AvalonDock that can be removed from my Model.Documents in order to be eventually be disposed when I hit its Close button?
For reference: This is my XAML of the AvalonDock:
<avalonDock:DockingManager
x:Name="DockingManager"
DocumentsSource="{Binding DocumentItems}"
ActiveContent="{Binding ActiveMainWindowViewModel,
Converter={StaticResource RestrictedClassConverter},
ConverterParameter={x:Type multiSimAnalysis:MainWindowViewModel},
Mode=TwoWay}"
DocumentClosing="DockingManager_DocumentClosing"
ActiveContentChanged="DockingManager_ActiveContentChanged">
<avalonDock:DockingManager.LayoutItemContainerStyleSelector>
<pane:PanesStyleSelector>
<pane:PanesStyleSelector.MainWindowViewLcStyle>
<Style TargetType="{x:Type avalonDock:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
<Setter Property="ToolTip" Value="{Binding Model.Title}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"/>
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
</Style>
</pane:PanesStyleSelector.MainWindowViewLcStyle>
</pane:PanesStyleSelector>
</avalonDock:DockingManager.LayoutItemContainerStyleSelector>
<avalonDock:DockingManager.LayoutItemTemplateSelector>
<multiSimAnalysis:PanesTemplateSelector>
<multiSimAnalysis:PanesTemplateSelector.MainWindowLcTemplate>
<DataTemplate>
<multiSimAnalysis:MainWindowViewLc />
</DataTemplate>
</multiSimAnalysis:PanesTemplateSelector.MainWindowLcTemplate>
</multiSimAnalysis:PanesTemplateSelector>
</avalonDock:DockingManager.LayoutItemTemplateSelector>
<avalonDock:DockingManager.Theme>
<avalonDock:VS2010Theme/>
</avalonDock:DockingManager.Theme>
<avalonDock:LayoutRoot>
<avalonDock:LayoutPanel Orientation="Horizontal">
<avalonDock:LayoutAnchorablePane DockWidth="400">
<avalonDock:LayoutAnchorable Title="Scope(s) selection" x:Name="PanelScopeSelection" IsVisible="True">
<scopeSelection:UserControlSelectStudyScope x:Name="ToolScopeSelection"/>
</avalonDock:LayoutAnchorable>
</avalonDock:LayoutAnchorablePane>
<avalonDock:LayoutDocumentPane/>
<avalonDock:LayoutAnchorablePane DockWidth="150">
<avalonDock:LayoutAnchorable Title="Properties" x:Name="PanelScopePropertyGrid">
<!--<multiSimAnalysis:UserControlPropertyGrid x:Name="ToolPropertyGrid" />-->
<xctk:PropertyGrid x:Name="ToolPropertyGrid" SelectedObject="{Binding ActiveObject}" />
</avalonDock:LayoutAnchorable>
</avalonDock:LayoutAnchorablePane>
</avalonDock:LayoutPanel>
</avalonDock:LayoutRoot>
</avalonDock:DockingManager>
I actually find an unacceptable workaround.
It is really twisted.
I only give that as reference. There should be a clean way to do it.
// ************************************************************************
private void DockingManager_DocumentClosing(object sender, Xceed.Wpf.AvalonDock.DocumentClosingEventArgs e)
{
e.Document.CanClose = false;
DocumentModel documentModel = e.Document.Content as DocumentModel;
if (documentModel != null)
{
Dispatcher.BeginInvoke(new Action(() => this.Model.DocumentItems.Remove(documentModel)), DispatcherPriority.Background);
}
}
I have found that on a LayoutDocument or a LayoutAnchorablePane, applying both this setting works: CanClose="False" or CanFloat="False".
It removes the Close button.
<avalonDock:LayoutDocument Title="Board"
ContentId="Board"
CanClose="False"
CanFloat="False">
</avalonDock:LayoutDocument>
Register for IsVisibleChanged.
void layoutFPR_Hidden(object sender, EventArgs e)
{
LayoutAnchorable window = (LayoutAnchorable)sender;
YourClass content = window.Content as YourClass;
// Close the object
content = null;
((LayoutAnchorable)sender).Close();
}
I am using a WPF treeview, when i click on a node\item once it gets selected. When the user clicks on the selected node the second time i want this node\item to get deselected i.e. i should be able to get the event. IsSelected is not called if i click on the selected node\item that is already selected. How do i get it to work?
<TreeView Grid.Column="0" Grid.Row="1" ItemsSource="{Binding source}" Name="mytreeview">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding displaytext}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
and in my view model i have
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (value != _isSelected)
{
_isSelected = value;
if (_isSelected)
{
//my logic
}
this.OnPropertyChanged("IsSelected");
}
}
}
if (value != _isSelected)
Assuming that the UI is even trying to set something, that line is blocking your toggle logic. Something like this should fix at least that part.
set
{
if (value != _isSelected)
{
_isSelected = value;
this.OnPropertyChanged("IsSelected");
}
else if(_isSelected)
{
IsSelected = false;
}
}
Otherwise the UI is checking the selection before setting the value and you'll need to handle it through some other user interaction like handling deselection on click.
I know this is a bit late but I've recently had the same requirement (i.e. unselecting a selected TreeViewItem on the second click) and I solved it by declaring an event handler for the 'MouseLeftButtonUp' event in a 'Style' entry for the ItemContainerStyle of the TreeView as follows:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseLeftButtonUp" Handler="TreeViewItem_MouseLeftButtonUp"/>
</Style>
</TreeView.ItemContainerStyle>
The event handler in the code behind was as follows:
private TreeViewItem prevTVI;
private void TreeViewItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TreeViewItem tvi = (TreeViewItem)sender;
if (tvi == this.prevTVI)
{
this.prevTVI = null;
if (tvi.IsSelected)
tvi.IsSelected = false;
}
else
this.prevTVI = tvi;
e.Handled = true;
}
Now, I would like to ask if anyone thinks this approach breaks the MVVM pattern? I personally don't think so as the event handler is only concerned with the View and its objects not anything else but I would like to hear what others have to say, especially if someone has an alternative.
The IsSelected property is only changed when you select a new item. Clicking on the same item twice will normally have no effect. You would need to register the MouseDown event on the TreeView, and then force the item to be deselected in the code-behind.
I am making this app for windows phone 7, what I do is retrieve all the images from camera roll, saved pictures and other folder and display them in the listbox inside a wrap panel so they are displayed side by side....the thumbnail of the images is actually displayed hear.....
but as the number of images are increasing UI gets very slow and scrolling takes time...
I read many post and other question I think data virtualization or lazy loading is what I need but I am not understanding how can I use it, I saw the post from shawn oster and peter torr.....
I use a backgroundworker to load the images...
here's how...
void backroungWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
try
{
using (IsolatedStorageFileStream imageStream = fileStorage.OpenFile(filepath, FileMode.Open))
{
var imageSource = PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
var item = new ImageToName { bmp = bitmapImage, FileName = fileName };
vltBitmapImage.Add(item);
imageStream.Dispose();
imageStream.Close();
}
}
catch
{
Exception x = new Exception();
}
}
if (vltBitmapImage.Count() != 0)
{
lone.Visibility = Visibility.Collapsed;
this.vaultbox.ItemsSource = vltBitmapImage;
}
else
lone.Visibility = Visibility.Visible;
});
}
any help is greatly appreciated.....
sorry for being a noob...
Try this sample from code project, it explain how it work and comes with a full sample project
See: Loading Data when the User scrolls to the end of the list
If you want to add a Lazy load to a listbox you must setup a listner for your list box and change the way you load data into your data model, so first lest set up at the XAML code for a listbox:
In your page resource add this style, and note the loaded event its include ("ScrollViewer_Loaded"):
...
<phone:PhoneApplicationPage.Resources>
<Style x:Key="BusinessListBoxStyle"
TargetType="ListBox">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{StaticResource PhoneForegroundBrush}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="Padding"
Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer x:Name="scrollViewer"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
Loaded="ScrollViewer_Loaded">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>
...
The add a reference to your style for the listbox, and bind your itemsSource to a list of items from your viewModel.
...
<ListBox x:Name="myList"
ItemsSource="{Binding myDataSource}"
Style="{StaticResource BusinessListBoxStyle}">
...
Next you have to set some code that loads data into the datamodel, when you reach the end of the list element in the list (the datamodel start loading more data into the mode, adds more items) Lazyloading!!
The way i normaly do this is by listing to the vertical offset of the listbox's scrollbar, and if its is about 1/4 from the edge i starts loading more items into the datamodel.
In the ScrollViewer loaded handler i set up at VertialOffset listener, by using the DependencyProperty, see code below:
public static readonly DependencyProperty ListVerticalOffsetProperty =
DependencyProperty.Register(
"ListVerticalOffset",
typeof(double),
typeof(MyPage),
new PropertyMetadata(new PropertyChangedCallback(OnListVerticalOffsetChanged))
);
private ScrollViewer _listScrollViewer;
private void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
_listScrollViewer = sender as ScrollViewer;
Binding binding = new Binding();
binding.Source = _listScrollViewer;
binding.Path = new PropertyPath("VerticalOffset");
binding.Mode = BindingMode.OneWay;
this.SetBinding(ListVerticalOffsetProperty, binding);
}
public double ListVerticalOffset
{
get { return (double)this.GetValue(ListVerticalOffsetProperty); }
set { this.SetValue(ListVerticalOffsetProperty, value); }
}
private double _lastFetch;
private static void OnListVerticalOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyPage page = obj as MyPage;
ScrollViewer viewer = page._listScrollViewer;
if (viewer != null)
{
if (page._lastFetch < viewer.ScrollableHeight)
{
// Trigger within 1/4 the viewport.
if (viewer.VerticalOffset >= (viewer.ScrollableHeight - (viewer.ViewportHeight / 4)))
{
page._lastFetch = viewer.ScrollableHeight;
MyViewModel _tmpviewmodel = page.DataContext as MyViewModel;
if ((_tmpviewmodel != null) && (_tmpviewmodel.HasMoreItems))
_tmpviewmodel.GetMoreItems();
}
}
}
}
Note here i make use of a MyViewModel, that holds all the items the listbox i binded to, and has methods for load items from a database, the isolatedstore, the web or what ever your needs are.
You just have to find your way of only load a part of your data into the viewmodel. I your case i will first load a list of all the files you need to load (that is just to retreive the list from GetFileNames in the IsoLatedStore). Then mayby only loads 20 pics at the time!