This is a really weird bug. I have no idea why it could be happening. I know that posting it here is a bit of a long-shot, but I'm out of other ideas.
I have two ListBoxs that act as menus.
<ListBox Margin="56,8,15,0" FontSize="64"
ItemsSource="{Binding FavoriteSections}"
SelectionChanged="MenuList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Remove" Click="FavoritesContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="sectionList" Margin="56,8,15,0" FontSize="64"
SelectionChanged="MenuList_SelectionChanged"
ItemsSource="{Binding SectionViewModels}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Add to favorites" Click="SectionContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The bug exists across both of them.
When the selection changes on either menu, this method is called:
void MenuList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 0)
{
return;
}
Uri page = null;
object selected = e.AddedItems[0];
if (selected is NavigableItem)
{
NavigableItem selectedItem = (NavigableItem)selected;
page = selectedItem.Page;
}
else if (selected is SectionViewModel)
{
SectionViewModel selectedVM = (SectionViewModel)selected;
page = selectedVM.Section.Page;
}
Debug.Assert(page != null, "What is the type of `selected`?");
// if I comment out this line, the problem goes away:
NavigationService.Navigate(page);
ListBox selectedBox = (ListBox)sender;
selectedBox.SelectedIndex = -1;
}
If I comment out the NavigationService.Navigate() line, the problem goes away. If I replace the line with a different URI, the problem remains.
About 70% of the time, when I click on a menu item, the content jumps all over the page. (The remaining 30%, no bug occurs.) It happens too quickly to see what's really going on, but different UI elements overlap each other.
This only occurs the first time I click on something in those menus during the app's lifetime. If I hit "back" then select a menu item again, the problem will not occur.
What could be happening here? I really have no idea. The code-behind doesn't have a OnNavigatedFrom method, so I don't think it's a problem there.
I'm using Silverlight for Windows Phone 7
Update: Mysteriously, I can't seem to reproduce this in the debugger - only after deploying the app and running it in the emulator unattached. ???
Update 2: The bug appears when NavigationService.Navigate() is called from the Click event handler of a button, as well:
<Button Content="Foo" Click="Button_Click" Grid.Row="0"/>
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/Views/sections.xaml?section=43", UriKind.Relative));
}
Looks like the bug has to do with the navigation, not the UI element used to trigger the call.
Update 3: More weirdness. Still not able to reproduce the app while the debugger is attached. If I make the loading progress bar always collapsed, the bug disappears:
<ProgressBar x:Name="LoadingProgressBar"
IsIndeterminate="True"
Visibility="Collapsed"
Style="{StaticResource PerformanceProgressBar}"
VerticalAlignment="Top"/>
Alternatively, commenting out this line in code-behind makes the bug disappear:
LoadingProgressBar.Visibility = Visibility.Collapsed;
I really don't understand what's going on here. That line of code is not executed when the page is navigated from.
Here is the full XAML of the control that's getting messed up:
<ProgressBar x:Name="LoadingProgressBar"
IsIndeterminate="True"
Visibility="Collapsed"
Style="{StaticResource PerformanceProgressBar}"
VerticalAlignment="Top"/>
<TextBlock x:Name="DownloadFailed"
Visibility="Collapsed"
Style="{StaticResource disabledText}"
Margin="56,8,8,-8" >
FooBar.com could not be reached. Do you have a network connection?
</TextBlock>
<ListBox x:Name="sectionList" Margin="56,8,15,0" FontSize="64"
SelectionChanged="MenuList_SelectionChanged"
ItemsSource="{Binding SectionViewModels}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Add to favorites" Click="SectionContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</controls:PivotItem>
The problem lies in your usage of the Indeterminate ProgressBar. All its animations are done on the UI thread, and not the Compositor thread, as is the usual practise. Since you are already using the Windows Phone Toolkit, you can easily replace your ProgressBar with the PerformanceProgressBar offered by the toolkit. This should solve your problems.
Before I begin, let me say that I don't have a lot of experience with Windows Phone, so my answers are based on more generic WPF knowledge, so forgive me if I'm overlooking specifics of the platform, or am referencing features not available.
Some diagnostic questions (sorry this isn't a direct answer):
Firstly, it does seem like Navigate is calling a lot of layoutUpdates. I haven't yet seen what type of container is containing the pages you're updating, but it is worth asking, is that also being disrupted or only the menus?
Secondly, could you try specifying your itemPanel explicitly? You're expecting them to be virtualizingStackPanels, but you may find that some parent object in your visual hierarchy is creating a different inheritance scenario.
You have these in a grid, which is meant to size to its content, or take the default size (100x100 in normal WPF) or take sizing from its parent, which without knowing how you've specified the grid, or the grid's parent, it's difficult to know its behaviour. Furthermore, Grids automatically z-order their children according to the order in which they were added. Can you determine whether it is just the layout of the lisboxes that is being disturbed, or whether it is the entire grid? Or, is it larger than that?
If you attach to the layoutUpdated() event of the listboxes, grid, or grid's parent, you should be able to look at the stacktraces that lead you there - it sounds to me that you'll find that layoutUpdated() is firing more than you'd like it to. Further, you'll be able to output the heights and widths (ActualHeight etcetera of course) during those steps so that you can see when exactly those changes happen.
I hope that some of these diagnostic steps might help you reach an answer.
Related
I have a screen that shows 2 collections.
On the left side, I display a list of sections. By default, the first section is selected. If you click on another section then that becomes selected instead.
On the right side, I have a list of associated questions for that section in a one to many relationships.
So each question belongs to a section and a section can have 1 to many questions.
Some questions are required to have an answer, and some are optionally answered.
To make it easy for the user to find the required questions, a red asterisk is displayed next to the answer textbox. When that question gets answered it disappears.
Also, I need to show an asterisk for each section where there are unanswered questions. Once they are all answered the asterisk for the section also disappears.
The visual tree is of this format;
SurveyPageViewModel - SurveyViewModel - SectionViewModel - QuestionViewModel
The code below shows the QuestionViewModel (which contains the answer property) and the XAML code is in a DataTemplate so I do not think there is a way up the Visual Tree to update the section.
So my (simplified) handler code for my answer property looks like this;
private string _answer;
public string Answer
{
get
{
return _answer;
}
set
{
if (SetProperty(ref _answer, value))
{
this.IfQuestionSetCheckIfAnswered(this.IsRequiredOnScreenAnswer);
}
}
}
private void IfQuestionSetCheckIfAnswered(bool value)
{
if (this.IsRequired && string.IsNullOrWhiteSpace(this.Text) == false)
{
this.EventAgg.GetEvent<RequiredAnswerUpdatedEvent>().Publish(value);
}
}
and my XAML for the question/answer;
<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Margin="0, 0, 0, 5">
<TextBox Grid.Row="1"
Text="{Binding Path=Answer, Mode=TwoWay}"
MinWidth="300"
IsReadOnly="{Binding Path=IsReadOnly}" />
<TextBlock Text="*" FontSize="40" FontWeight="Bold"
Style="{StaticResource ResourceKey=RequiredSignal}" Margin="5, 0, 0, 0"
Visibility="{Binding Path=IsRequiredOnScreenAnswer, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}" />
</StackPanel>
I am using Prism and the EventAggregator pattern to update the Selected Section which is in a different ViewModel.
Now this works except for one important issue. I only want to update the section once the page is loaded. Currently, this event is fired both when the page is loaded AND when the answer changes.
How do I get this to work so the load is ignored?
I only want to update the section once the page is loaded. Currently this event is fired both when the page is loaded AND when the answer changes.
I'd define a command to activate the update once the page is loaded
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:InvokeCommandAction Command="{Binding ActivateCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
The above requires the Behavior SDK on WinRT (or the NuGet package on UWP), alternatively you could handle the Loaded event in code behind and relay it to the ViewModel as well.
Then ActivateCommand is supposed to set a bool IsLoaded member of the ViewModel so that it can be tested within your IfQuestionSetCheckIfAnswered method.
I would like to know if there's a way to implement a responsive Master/Detail page using only one. What I want is something exactly like the Project here:
https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlMasterDetail
Except for the detail that instead of using two pages and navigating from one to another I would only use one page.
Is there a way to do it? If so, could you link me a working example?
Except for the detail that instead of using two pages and navigating from one to another I would only use one page.
After going through the project, I found it implemented a responsive master/detail experience based on the size of the screen. When the app view is sufficiently wide, the master list and detail view should appear side by side in the same app page. However, on smaller screen sizes, the two pieces of UI should appear on different pages, allowing the user to navigate between them. From my point of view, I think this is a good solution for implementing a responsive master/detail experience.
Is there a way to do it? If so, could you link me a working example?
The project already shows how to implement responsive Master/Detail in UWP using only one page, but it implements more and that makes it a little complex to understand. So I make a simple example which directly shows how to implement responsive Master/Detail in UWP using only one page.
Following is the main steps:
First, create a ListView to show master information in xaml page:
<!--Master VIEW-->
<ListView x:Name="ItemListView" Margin="0,0,0,8">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Left" Margin="10,8,0,0">
<TextBlock Text="{Binding Title}" FontSize="25" Width="400" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Second, specify the details view that shows the details item related to the selection on the master list in the same xaml page:
<!--DETAILS VIEW-->
<StackPanel Grid.Column="1" x:Name="ContentPanelDetail" Margin="10,0,0,0" DataContext="{Binding SelectedItem, ElementName=ItemListView}">
<TextBlock Text="{Binding Title}" MaxHeight="80" FontSize="30" HorizontalAlignment="Left" Margin="0" />
<TextBlock x:Name="DetailTextBlock" FontSize="35" Text="{Binding Content}" HorizontalAlignment="Left" Margin="0,18,40,0" Width="500" Height="Auto" TextWrapping="Wrap" />
</StackPanel>
Then, set the ItemsSource for the ListView in code behind:
public MainPage()
{
this.InitializeComponent();
//set the ItemsSource for the ListView
ItemDetails messageData = new ItemDetails();
ItemListView.ItemsSource = messageData.Collection;
ItemListView.SelectedIndex = 0;
}
Last but not least, put Master View and Details View into a SplitView and use VisualStateManager to make it more responsive.
Here is the simple example and the output for your reference.
To implement Master/Detail pattern on your page, you don't have to do it yourself. Instead you can use MasterDetailsView control from UWP Community Toolkit, it does a lot work for you + it is well documented.
Note: For details section of the control, do not set background to null (NoSelectionContent will be visible).
something that I thought would be simple is turning out not to be, or I'm just not thinking hard enough :)
I have a page which I navigate to, in the OnNavigateTo event I set the SelectedIndex of a ListPicker and that works fine.
If I then touch the ListPicker and select a new value the OnNavigateTo event is fired again and the new value is overridden by the original value.
My initial thought was to simply check the parent page name and if it was the ListPicker then skip the initial setting but I can't seem to find where to get the parent page name from.
Any clues? or a better way I should be handling this?
Here's the XAML:
<toolkit:ListPicker x:Name="Status" Margin="10,549,163,-97" Header="Status" FullModeHeader="Status" ExpansionMode="FullScreenOnly" BorderBrush="Black" Foreground="Black" Grid.ColumnSpan="2" Visibility="Visible">
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
FontSize="43"
FontFamily="{StaticResource PhoneFontFamilyLight}"/>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
And here's the Loaded event:
private void AddNote_Loaded(object sender, RoutedEventArgs e)
{
this.TicketStatus.ItemsSource = ticketStatus();
string st;
if (NavigationContext.QueryString.TryGetValue("status", out st))
{
tStatus = st;
TicketStatus.SelectedIndex = GetStatus(tStatus);
}
}
Ok, worked around it but creating my own page list and manually adding and removing the pages I want to check for. Bit of a hack but it works :)
I am trying to build a 'comments control' (In a win8 XAML app, which is similar to Silverlight) which will load and render a comment tree for the user.
Each comment can have 0 or 1+ children comments (which recurses through each comment).
Each comment displays a set of info, including author, time, the comment itself, etc.
The approach I initially took to build this is to use a ListView which has a 'CommentItem' control binding.
<TextBlock Grid.Row="1" TextWrapping="Wrap" Text="{Binding commentText}" FontSize="11" FontFamily="Global User Interface" />
<ListView Grid.Row="2" x:Name="CommentRepliesList" ItemsSource="{Binding}" >
<ListView.ItemTemplate>
<DataTemplate>
<local:CommentItem Tag="{Binding}"></local:CommentItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The above will just recurse through each comment, apply the comment text, and then create a new commentitem for each comment child, recurse through again, etc.
The issue, however, is this is extremely slow and non-performant.
Does anyone know a more efficient way to do this? Is ListView the appropriate control to use for this?
I need to create a button with two lines of text:
The first one is Command Title like "Save"
The second one is a Description of the Command like "The application state will be saved"
So I have written the next xaml:
<Button Margin="0,128,0,0" Padding="10,5" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<StackPanel Margin="0" UseLayoutRounding="False">
<TextBlock FontSize="{StaticResource PhoneFontSizeMediumLarge}" FontFamily="{StaticResource PhoneFontFamilySemiBold}">Save</TextBlock>
<TextBlock Style="{StaticResource PhoneTextSubtleStyle}" Margin="0">The application state will be saved</TextBlock>
</StackPanel>
</Button>
This code working well except a one issue. The Description line becomes invisible when the button is pushed.
I'm sure the root cause is the low contrast color of the description line. But I don't know how to fix it.
Update: I have tried to use the PhoneTextSubtleStyle style but still have the same issue.
You could retemplate the Button (using the Control.Template property) to look different so that when pushed it no longer interferes with the content.
Could you try something like this
System.Windows.Visibility.Visible;
System.Windows.Visibility.Hidden;
or
System.Windows.Visibility.Collapsed
here is a link that will show an example of how to use this inside of a StackPanel
How to: Change the Visibility Property