UI Thread held up when using XAML Binding in a WinRT app - c#

I have a WinRT C# app with two ListViews. A simple way to represent it is that one ListView is a list of folder locations, and the second ListView details the directory structure inside that folder:
ListView1 ListView2 (Contents of Folder 2)
Folder 1 Folder a
|Folder 2| File a
Folder 3 File b
Folder 4 Folder b
Folder c
File c
File d
Something like that.
I have a main ObservableCollection, which is a collection of folders. Each folder contains its own ObservableCollection, which is the data used in the second ListView.
ListView1 and 2 are bound the same, essentially:
ItemsSource="{Binding Source={StaticResource folderViewSource}}"
ItemsSource="{Binding Source={StaticResource fileViewSource}}"
With the CollectionViewSources being defined so:
<!-- The list of folders, used by the main ListView -->
<CollectionViewSource
x:Name="folderViewSource"
Source="{Binding FolderBinding}" />
<!-- The files and folders for a specific folder, used by the Files ListView -->
<CollectionViewSource
x:Name="fileViewSource"
Source="{Binding ElementName=folderListView, Path=SelectedItem.Files}" />
ListView1 populates fine, no problems.
ListView2 does bind correctly (it shows the files for the selected folder in the first ListView), but the binding is done on the UI thread. On my PC, it's tolerable if unpleasant. On my Surface RT, it can take a good 2 seconds.
The template used in the second ListView is quite fiddly, but I filtered it down to just a string and it was still a bit jerky on the Surface - obviously I need to move it off of the UI thread.
My first thoughts were to move the binding away from the XAML and do it in code, but I couldn't see any way to improve it - Windows is still creating the UI elements on the UI thread regardless of what I do.
I also looked at ISupportIncrementalLoading but that seems to be for dynamically loading new items as you scroll, etc.
I've been stuck for a while now, I've had things keeping me busy but I want to get back to this, but I can't figure this out. Hopefully I'm missing something obvious.

Related

Control ItemsSource binding does not update on ClickOnce Web Published WPF app

I have a WPF application with an XAML Window containing a few Buttons and a DataGrid. When buttons are pressed, some logic is performed in the background which builds a list of some type and finally assigns the list to a (MVVM) model property "DataSource" to which an ItemsSource of the DataGrid is bound to like:
<DataGrid x:Name="DataGrid" Grid.Row ="10" ...
ItemsSource="{Binding Path=DataSource, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}">
When the content of DataSource is changed, DataGrid is aware of that and shows the new data. So far all good, application works as intended and with no problems. I can copy the bin output of build and sucessfully run it just about everywhere.
Then comes ClickOnce publish. If published so that it installs from, and is installed from an Unc network share, e.g. \SomeServer\SomePath\InstallFromDir\, everything still works fine.
But if published so that it installs from, and is installed from an online url, e.g. http://companydomain.com/InstallFromDir/, DataGrid simply does not update.
Nothing else is changed but the method of app and updates availability from unc to web.
What I gathered from debugging trace points still applies. The list in the background is built anew. It is assigned to the DataSource property (if I verify the contained data, it does contain the new data). But if I access the DataGrids ItemsSource it still contains the OLD, initial data.
I have tried re-assigning the DataSource to ItemsSource -- doesn't help. I have tried re-binding the data when it changes programatically, as in:
var sourceBinding = new System.Windows.Data.Binding
{
Path = new PropertyPath("DataSource"),
Mode = BindingMode.TwoWay,
NotifyOnSourceUpdated = true,
NotifyOnTargetUpdated = true
};
BindingOperations.SetBinding(((MainWindow)_window).DataGrid,
System.Windows.Controls.DataGrid.ItemsSourceProperty, sourceBinding);
and even changing the xaml so that it is ONLY bound in code:
<DataGrid x:Name="DataGrid" Grid.Row ="10" ... >
and it still works the same as described above: works as direct copy or unc install, but doesn't work as web install.
I am totally lost as to either finding the cause or finding the workarounds. Anyone encountered this before? Any ideas? Many thanks!
Update: I have initially thought this was a DataGrid issue, but I have since also tried replacing DataGrid with ListView, and it doesn't work either.

Change a section of a window upon click event

I have a treeview at the left side of the screen, and when I click on any of the TreeViewItem, I want the right side of the screen to change accordingly.
For example, clicking on 'Project' would display on the right half of the screen, a label for project name along with the project name in a text box, and a similar label-textbox pair for some other fields. Clicking on a sub-option of 'Project' such as 'Task 1' should change the right half of the screen such that instead of labels and textboxes for project name and details, it should now be for task name/details. Atm, I only care about label-textbox pairs but in the future I'll need some more sophisticated options, maybe buttons and tables.
What I thought of was to have a grid premade for each option, when I clicked on 'Project' there would be a grid which displays all the info for a Project. And when I then clicked on 'Task 1', the Project grid should be hidden and the Task grid should be displayed with the fields filled out.
Is this possible? What should I be using to create templates that I can then choose from?
Firoz already mentioned the important bit. A rough guess is that you're not using MVVM pattern, so to minimize the adaption effort, you could add a Content Control to your window and set the content of this control whenever a selection is made. You can put any User Control in there.
Using MVVM would mean you bind that Content Control to a property on your ViewModel (of type UIElement or UserControl) and set an instance whenever a bound selected values changes. Speaking of selected Value, I think the default TreeView is not really Binding-friendly, so you might end up with behaviours that do the binding for you.
What you are asking to do is quite easy and possible, but I don't think you are thinking quite big enough.
As your project grows and the number of different things that you want to show expands, then you are going to need to show and hide more and more controls. This is quite quickly going to get unmanageable. Instead think about some other controls deal with this, in some ways you are doing something very like a tabbed dialog, just with a hierarchical set of tabs.
A tabbed dialog has a panel and a set of tabs, when you click on each tab, the content of the panel changes. In fact you can create UserControls one for each specialised set of UI that you want to display, e.g. you could have a ProjectControl that displays all of your project textboxes, labels, buttons etc.
In addition WPF has this neat feature called DataTemplates, these define how a type of data should look when it is displayed. So if you where to have a
public class MyProject
{
public string Name {get;set;}
}
Then you could define
<DataTemplate DataType="{x:Type MyProject}>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
And WPF will automatically convert the data into to its visual form if you set it as the content of the tab panel.
However this type of displaying content in a panel is not the only WPF control that does this. There is also something called a NavigationFrame, which also can be used wrapped into a Window as a NavigationWindow. This control provides you ways to navigate to the next Page to display. Pages can be just like the UserControls in a tabbed dialog, but can also be URIs, enabling you to link in content from the web if you wish. In addition you can call NavigateTo from other controls enabling you build much more usable interfaces.
I worked through the process of building a full windows control panel style interface in
http://alski.net/post/2012/01/11/WPF-Wizards.aspx
and http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx
I've added later VS2012 style glows in
http://alski.net/post/2013/09/14/WPF-Re-creating-VS2012Office-2013-window-glow.aspx
And then released the entire source code as open source at
http://winchrome.codeplex.com/
This comes with support for embedding Navigation panels with
<WinChrome:SearchableNavigationWindow
x:Class="WinChrome.Win7Demo.MainWindow"
...
xmlns:WinChrome="clr-namespace:WinChrome;assembly=WinChrome"
Style="{StaticResource Win7NavigationWindow}">
<WinChrome:SearchableNavigationWindow.Navigation>
<view:Navigation x:Name="navigationTree"/>
</WinChrome:SearchableNavigationWindow.Navigation>
(Full source code)
Where the navigation window is embedded as, but can also be a TreeView.
<UserControl x:Class="WinChrome.View.Navigation" ...>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" Padding="12,0"
VerticalScrollBarVisibility="Auto" >
<StackPanel>
<Button
Margin="0,12,0,0" Style="{StaticResource LinkNavigatorButtonStyle}"
Content="Home"
Command="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Win7Demo:MainWindow}, AncestorLevel=1},
Path=GoHomeCommand}" />
</StackPanel>
</ScrollViewer>
(Full source code)

WPF ListView items drawn

I have Listview with templated items (which looked just like Windows Explorer). My problem is when I openning folder which contains a whole lot of files that ListView loads those files not smoothly.(see video: http://screencast.com/t/bY7ucELj9I).
Does someone has any idea how to solve it?
You may need to virtualize your ListView's Items. Example
<ListView Name="ListViewWithVirtualization" Height="100" Margin="5"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling" />
If you set VirtualizingStackPanel.VirtualizationMode = “Recycling” then each container will get reuse instead of destroy. You will get better performance by specifying VirtualizationMode to Recycling than basic default virtualization. See http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx

Editing ListBox with Live/Preview?

I am currently customizing a ListBox. With that I mean adding an image-element, second line-element etc. to one listItem.
The itemSource is a List in C#, therefore I have no preview of the items in Expression Blend/VS. And that's the pain.
Because I have always to edit the XAML and then deploy to check. And this goes on and on until the last pixel is correct.
Isn't there a way, of editing a ListBox with custom items (with a dynamic itemSource) live in Blend/VS?
That would really fasten up my developing.
If you want to see how your controls look like in design time, you must use SampleData. There are several ways to do it, it depends on your framework.
Let's say you have a page named MainPage.xaml. If you don't have view model already, create a new one and name it MainViewModel.cs. Define all public properties that will be used for binding.
Once you have your view model, create new file in a folder named SampleData and name it MainViewModelSampleData.xaml.
Now, in the MainPage.xaml add the following attribute to the page element:
d:DataContext={d:DesignData Source=SampleData/MainViewModelSampleData.xaml}
Also set Build Action for MainViewModelSampleData.xaml to DesignData.
Now, if you want to display data in your MainPage, you need to define all properties in the sample data file. For example:
// view model contains public properties Title of type string and Children of type
// PersonViewModel which contains properties Name and Age (string and int respectively)
<local:MainViewModel xmlns:local="clr-namespace:myapp"
Title="Title">
<local:MainViewModel.Children>
<local:ChildViewModel Name="John" Age="31" />
</local:MainViewModel.Children>
</local:MainViewModel>
You should now see your page filled with data in your design view. This way by using MVVM you can create mock data quickly. That will ensure that you can design your view around existing data without running the application.
Read more on the following links:
31 Days of Mango | Day #18: Using Sample Data
Modify sample data
Generate sample data
Using Blend Sample data at Design time and real data at Runtime
I now know how to do that.
If anyone if you guys ever stumble upon this problem, do this:
Copy all the XAML you wrote in the stackpanel of your itemtemplate
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
//...
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
And copy it up to <ListBox> //Here </ListBox>
There you can edit it in the designer.
And when you're done, just copy the code back to the StackPanel.

Can I bind an image from one form to the control on another

I have two forms, one is an 'editor' type form where I have the ability to select multiple images from the users pc and upload them into the local database where they are stored. This works fine, I can access them and see them populated in my listbox.
However, I also want to be able to show these images on another form, for the sake of saving time - Yes, I must use another form. I am fairly new to data binding, but I still understand it enough to have implemented it quite extensively, I just can't figure out how to databind from the image object on form 2, to the listbox on Form 1. The end idea was to create a slideshow type application (only 1 part of the application).
By Form I assume Window, so there are multiple way to do this...
MVVM - This will have a view model instance bound to window 1 that will hold the URI of the selected images and the same view model instance can be bound to the other window and it will show the images on the other.
You can refer two elements from two forms if one of the forms has been marked as the Owner of another.
E.g. if Window1 is the OwnerWindow of Window2 like so...
window2.Owner = window1;
window2.Show();
On Window1, bind the Window.Tag property with the element's selected items property...
<Window x:Class="...Window1"
...
Tag="{Binding SelectedItems, ElementName=MyListBox, Mode=OneWay}">
<ListBox x:Name="MyListBox" .... />
</Window>
Then on Window2 you could use the data binding to data context for acheiving the same via that Owner.Tag property
<Window x:Class="...Window2"
...>
<ListBox x:Name="ThumbnailListBox"
DataContext="{Binding Owner.Tag,
RelativeSource={RelativeSource
AncestorType={x:Type Window}}"
ItemsSource="{Binding}" />
</Window>
Hope this helps...
You can bind directly from the database in the second form. Since you have the images stored in the local database by now, you can easily retrieve them in the second form.

Categories