ToolBar OverflowPanel remains open - c#

I have a ToolBar with a ItemsTemplate which works fine until the OverflowPanel is Available.
The OverflowPanel does not close if i select one of the context actions.
The Problem only occurs if the Items are added via the ItemsSource binding:
<ToolBar ItemsSource="{Binding ContextActionViewModels}"
Background="Transparent"
ToolBarTray.IsLocked="True"
FocusManager.IsFocusScope="False">
<ToolBar.ItemTemplateSelector>
<views:ContextActionTemplateSelector>
<views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<DataTemplate DataType="{x:Type viewModels:SimpleContextActionViewModel}">
<Button Name="Button"
Command="{Binding ActionCommand}"
Style="{StaticResource ToolBarButtonStyle}"
ToolTip="{userInterface:Translation Binding={Binding ToolTip}}">
<ContentControl Template="{Binding Icon,Converter={StaticResource NameToResourceConverter}}"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</DataTemplate>
</views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<!-- Multiple DataTemplates follow!-->
Why is the DataTemplate / ItemTemplteSelector not working properly. While hardcoded Buttons in XAML work properly?
I uploaded a full sample that illustrates what is not working here:
Just Resize the window and try invoking one off the buttons in the OverflowPanel. While the 'ICommand' is executed properly the Popup stays open.

In the .NET framework source you can find the method that handles the closing behavior of OverflowPanel for ToolBar class:
private static void _OnClick(object e, RoutedEventArgs args)
{
ToolBar toolBar = (ToolBar)e;
ButtonBase bb = args.OriginalSource as ButtonBase;
if (toolBar.IsOverflowOpen && bb != null && bb.Parent == toolBar)
toolBar.Close();
}
When you define a DataTemplate and use ItemsSource property, the Parent property of the created button becomes null and the if check fails. This is the expected behavior of DataTemplate as described here:
For templates, the Parent of the template eventually will be null. To get past this point and extend into the logical tree where the template is actually applied, use TemplatedParent.
As a solution you can set the IsOverflowOpen property to false when you click any of the buttons:
<ToolBar Name="SomeToolBar" ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate DataType="local:ItemViewModel">
<Button Command="{Binding Command}" Content="{Binding Name}" Click="ButtonBase_OnClick"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SomeToolBar.IsOverflowOpen = false;
}

Related

Get previous page name

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 :)

Hold Event Longlistselector

Hej
I want to create a standard holdevent. When you hold an element, there would appear some options you could chose like a new list.
How do you create this, is it just simply done with a popup or is there a smarter way?
Extra
After finding the answer, see answer below, some nice info is:
Put the context creation inside the hold event.
Then you can change to different contextmenus depending on the item. You can get the item that was holded by the following
private void StackPanel_Hold(object sender, GestureEventArgs e)
{
ItemViewModel itemViewModel = (sender as StackPanel).DataContext as ItemViewModel;
string t = itemViewModel.LineOne;
}
And
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Height="78" Hold="StackPanel_Hold">
<TextBlock Text="{Binding LineOne}" />
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
A good link for easy implementation is also youtube link below, replicated here :
Youtube
A ContextMenu is one option..
http://blogs.msdn.com/b/msgulfcommunity/archive/2013/05/19/windows-phone-toolkit-context-menu-getting-selected-item-within-a-long-list-selector.aspx

wpf ListBox Control: simple GetSelectedValue / item-value

List box is not binded just a Combobox replacement (values are exposed)
Xaml
<ListBox SelectionChanged="LBX_AddTaskOptions_SelectionChanged" HorizontalAlignment="Left" Margin="19,29,0,0" Name="LBX_AddTaskOptions" VerticalAlignment="Top" Width="125" FontWeight="Bold" Background="Beige">
<ListBoxItem Background="Beige" FontWeight="Bold" v>
<StackPanel Orientation="Horizontal">
<TextBlock Text="internet"></TextBlock>
<Image Source="Images\IE_BlackRed.png" Height="30"></Image>
</StackPanel>
</ListBoxItem>
<ListBoxItem Background="Beige" FontWeight="Bold">
<StackPanel Orientation="Horizontal">
<TextBlock Text="localFolder"></TextBlock>
<Image Source="Images\Folder_Black.png" Height="30"></Image>
</StackPanel>
</ListBoxItem>
</ListBox>
CodeBehind
private void LBX_AddTaskOptions_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var SelItm = LBX_AddTaskOptions.SelectedItem.ToString();
MessageBox.Show(Sel);
}
i have searched for that question, though answers are only for complex issues
as i am fresh .net Developer, i know all methods to extract DDL text/value
i even made extentions , though couldn't figure how to do this simple value extraction
shouldn't it be simple ?
messageBox shows the name of control (:
This isn't quite the right approach for XAML. You don't want to list out the markup for each item -- instead, use an ItemTemplate to define how it should look, and use bindings to render the actual item:
<ListBox SelectionChanged="LBX_AddTaskOptions_SelectionChanged" Name="LBX_AddTaskOptions">
<ListBox.ItemTemplate>
<ListBoxItem Background="Beige" FontWeight="Bold" v>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Image Source="Images\IE_BlackRed.png" Height="30" />
</StackPanel>
</ListBoxItem>
</ListBox.ItemTemplate>
</ListBox>
Bind the ListBox ItemsSource to the model data itself (ie, the array of strings in this case). Now, eventually you'll probably want to use a view model, but you can also add the items from code behind on load:
string[] ListBoxItems = new string[] { "internet", "local folder" };
LBX_AddTaskOptions.ItemsSource = ListBoxItems;
This should result in SelectedValue giving you the correct value.
Footnote -- you could get the selected value using the markup you've written out in the question -- but it would be ugly and would defeat the whole purpose of XAML. You'd need to cast SelectedItem to a ListBoxItem, then get its child and cast that to a StackPanel, get its children, etc, you get the idea. And then, of course, if the markup changes at all, the code you just wrote is no longer valid.
The item that you are getting in your selected value is a ListBoxItem with a control inside it. If you want to extract the value like the text then you have to do this
private void LBX_AddTaskOptions_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var SelItm = LBX_AddTaskOptions.SelectedItem as ListBoxItem;
var StackPanel = SelItm.Content as StackPanel;
foreach (var child in StackPanel.Children)
{
if(child is TextBlock)
{
MessageBox.Show((child as TextBlock).Text);
}
}
}
You have to sort of dig into the control to get the actual text. There are a lot of ways to get the value but this is the pretty basic one.
Calling ToString() method will just convert the current object as a string which is a ListBoxItem.

Binding one control to another's DataContext

I bind my wpf window to app layer class (WindowVM.cs) using DataContext in Window.xaml.cs constructor (DataContext = WindowVM). But, one control (btnAdd) I want to bind to Window.xaml.cs property. So in Window.xaml.cs constructor I add this.btnAdd.DataContext. This is Window.xaml.cs constructor and property to which I want bind Button btnAdd:
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
public RelayCommand Add
{
get
{
return _add == null ? _add= new RelayCommand(AddPP, CanAddPP) : _add;
}
set
{
OnPropertyChanged("Add");
}
}
Xaml looks like this (class PP is WindowVM property):
<TextBox Name="txtName" Text="{Binding PP.Name, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSurname" Text="{Binding PP.Surname, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding Add}" Content="Add" ... />
And - everything works, but console output this:
BindingExpression path error: 'Add' property not found on 'object' ''WindowVM'...
In next calls there isn't any console output error for property Add.
Now I am a little bit confused because of this error. Is this error because of first DataContext (to WindowVM), because there isn't property Add, but with line this.btnAdd.DataContext property Add is found and it's the reason that it works?
Simply set the DataContext of the Button in the XAML using a RelativeSource:
<Button Command="{Binding Add}" Content="Add" DataContext="{Binding Add, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
I had this problem and I know this is an oldish post but I think this might help someone who stumbles on this in the future.
what I did was declare the viewmodels as resources
<Page.Resources>
<local:LocationListViewModel x:Key="LocationList" />
<local:LocationNewViewModel x:Key="NewLocation" />
<code:BinaryImageConverter x:Key="imgConverter" />
</Page.Resources>
then which ever control I wanted to be associated with said viewmodel I added this to their datacontext
<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations"
Header="Locations"
DataContext="{StaticResource ResourceKey=LocationList}">....
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Image x:Name="imgSettingsLocationMapNew" x:Uid="imgSettingsLocationMapNew"
Source="{Binding Map, Converter={StaticResource imgConverter},
Mode=TwoWay}"
DataContext="{StaticResource ResourceKey=NewLocation}" />
So in my example above I have Listview bound to the list viewmodel and I create a new single location for my new entry. You will notice that by creating it as a resource I can bind the tabitem and the image (which is not a child of the tab item) to the new location viewmodel.
My command for the addnew location is in the new location viewmodel.
<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd"
Header="Add New"
DataContext="{StaticResource ResourceKey=NewLocation}">....
<Button x:Name="btnSettingsLocationSaveAdd" x:Uid="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0"
VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}" HorizontalAlignment="Right" Width="75"
Command="{Binding AddCommand}" />.....
Which is the child of the tabitem I bound to the new location viewmodel.
I hope that helps.
When you set the DataContext-Property, your Window resets the Bindings of it's child controls. Even the Binding of your button.
At this Point (before "button.DataContext = this" is evaluated) "Add" is searched in WindowVM. After this you set the Window class as buttons DC, and everything works fine.
To avoid the initial error, swap two lines from this
public Window()
{
InitializeComponent();
DataContext = WindowVM;
this.btnAdd.DataContext = this;
}
to this
public Window()
{
InitializeComponent();
this.btnAdd.DataContext = this;
DataContext = WindowVM;
}

Silverlight: Layout changes when I call NavigationService.Navigate

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.

Categories

Resources