How can i mark menuitem in Application Bar. I want to get, for example, this:
You can change the template of the MenuItem.Header, as described in this tutorial. For example:
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="AddItem"/>
<!-- a templated menu item -->
<toolkit:MenuItem>
<toolkit:MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Delete Item"/>
<Image Source="Images/appbar.delete.rest.png"/>
</StackPanel>
</toolkit:MenuItem.Header>
</toolkit:MenuItem>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
I'd say it can't be done. The menu items are just calls to functions and can do nothing but show the corresponding text and call the corresponding method when activated. I've found some examples of people hacking extra functionality into them (updating the text or changing the function calls, to some extent), but nothing beyond that would be possible.
Notice that what you can do with the AppBar is limited by the functionality offered by IApplicationBar and, in your case, ApplicationBarMenuItem. And none of them expose anything even close to your requirements.
You can, however, enable and disable menu items, although I don't think that's what you want.
Related
I'm trying to find the best solution for a TabControl that both support a close button on each TabItem, and always show a "new tab button" as the last tab.
I've found some half working solutions, but i think that was for MVVM, that I'm not using. Enough to try to understand WPF =)
This is the best solution I've found so far:
http://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF
A solution that i actually understand. But the problem is that it is using the ItemsSource, and i don't want that. I want to bind the ItemsSource to my own collection without having to have special things in that collection to handle the new tab button.
I've been search for days now but cant find a good solution.
And I'm really new to WPF, otherwise i could probably have adapted the half done solutions I've found, or make them complete. But unfortunately that is way out of my league for now.
Any help appreciated.
I have an open source library which supports MVVM and allows extra content, such as a button to be added into the tab strip. It is sports Chrome style tabs which can tear off.
http://dragablz.net
This is bit of a dirty way to achieve the Add (+) button placed next to the last TabItem without much work. You already know how to place a Delete button next to the TabItem caption so I've not included that logic here.
Basically the logic in this solution is
To bind ItemsSource property to your own collection as well as
the Add TabItem using a CompositeCollection.
Disable selection of
the Add(+) TabItem and instead perform an action to load a new tab when it
is clicked/selected.
XAML bit
<TextBlock x:Name="HiddenItemWithDataContext" Visibility="Collapsed" />
<TabControl x:Name="Tab1" SelectionChanged="Tab1_SelectionChanged" >
<TabControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding DataContext.MyList, Source={x:Reference HiddenItemWithDataContext}}" />
<TabItem Height="0" Width="0" />
<TabItem Header="+" x:Name="AddTabButton"/>
</CompositeCollection>
</TabControl.ItemsSource>
</TabControl>
The code behind
private void Tab1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Contains(AddTabButton))
{
//Logic for adding a new item to the bound collection goes here.
string newItem = "Item " + (MyList.Count + 1);
MyList.Add(newItem);
e.Handled = true;
Dispatcher.BeginInvoke(new Action(() => Tab1.SelectedItem = newItem));
}
}
You could make a converter which appends the Add tab. This way the collection of tabs in you viewmodel will only contain the real tabs.
The problem is then how to know when the Add tab is selected. You could make a TabItem behavior which executes a command when the tab is selected. Incidentally I recommended this for another question just recently, so you can take the code from there: TabItem selected behavior
While I don't actually have the coded solution, I can give some insight on what is most likely the appropriate way to handle this in a WPF/MVVM pattern.
Firstly, if we break down the request it is as follows:
You have a sequence of elements that you want to display.
You want the user to be able to remove an individual element from the sequence.
You want the user to be able to add a new element to the sequence.
Additionally, since you are attempting to use a TabControl, you are also looking to get the behavior that a Selector control provides (element selection), as well as an area to display the element (content) which is selected.
So, if we stick to these behaviors you'll be fine, since the user interface controls can be customized in terms of look and feel.
Of course, the best control for this is the TabControl, which are you already trying to use. If we use this control, it satisfies the first item.
<TabControl ItemsSource="{Binding Path=Customers}" />
Afterwards, you can customize each element, in your case you want to add a Button to each element which will execute a command to remove that element from the sequence. This will satisfy the second item.
<TabControl ...>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CustomerId}" />
<Button Command="{Binding Path=RemoveItemCommand, Mode=OneTime,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
<TabControl.ItemTemplate>
</TabControl>
The last part is a bit more difficult, and will require you to actually have to create a custom control that inherits from the TabControl class, add an ICommand DependencyProperty, and customize the control template so that it not only displays the TabPanel, but right next to it also displays a Button which handles the DependencyProperty you just created (the look and feel of the button will have to be customized as well). Doing all of this will allow you to display your own version of a TabControl which has a faux TabItem, which of course is your "Add" button. This is far far far easier said than done, and I wish you luck. Just remember that the TabPanel wraps onto multiple rows and can go both horizontally or vertically. Basically, this last part is not easy at all.
I heed to create wizard and in the wizard I have tab control which have to call to the user control according to the context,I need to create the wizard which will able to invoke
different pages according to the user selection ,currently I call to the pages as follows which I think is not the right way,any Idea how should I do it via code (not in the xaml )i.e. according to some decision invoke the suitable page to the tab control.
this is the xaml:
<Border Grid.Column="1" Name="MainBorder">
<TabControl x:Name="MainTabControl" Height="638" VerticalAlignment="Bottom">
<TabItem Visibility="Collapsed" >
<Frame Source="page1.xaml" />
</TabItem>
<TabItem Visibility="Collapsed" >
<Frame Source="page2.xaml"/>
</TabItem>
<TabItem Visibility="Collapsed" Header="Step 3">
<TextBlock Text="Page 3"/>
</TabItem>
<TabItem Visibility="Collapsed" Header="Step 4">
<TextBlock Text="Page 4"/>
</TabItem>
</TabControl>
</Border>
UPDATE
I was tried in the main window like the following without success
create new tab by code and add to it the page 1 and then add it to the MainTabControl
TabControl tabControl = new TabControl(new Page1());
MainTabControl.add..
.
there is no add in the main tab control
For this scenario, I would use a Frame rather that tabs. The frame allows you to manage the flow of it's content via the NavigationService. You can use Uri's to display a page via the Frame.Source property, or a FrameworkElement via the Frame.Content property. Both are DependencyProperties and can therefore be bound to.
Paul Stovel wrote an excellent blog on this called WPF Navigation. Everything you need to create a wizard from a frame can be found in this blog, including passing values between pages and templating of the Frame to simply handle the display of navigation buttons.
I would agree with Mark, it is a lot easier to use NavigationWindows than TabControls.
I've worked on a lot of interfaces like this and written up some of the basic things with,
WPF Wizards, Part 1
WPF Wizards, Part 2
Then more recently I worked out how to get the styling just right
Styling Wizards
In fact I've released the styling and examples as open source at
WinChrome
There is some simple example code including use of a navigation list to the left with,
WinChrome.Win7Demo
Hope this helps
I have the following code that adds a context menu to a textbox on the UI. The user is supposed to be able to bring up the context menu and select a new units to be used. So the method CurrentUnits in my view model is bound to the textbox. I want a context menu populated by all the potential units. So the method Units in my view model returns a string[] of unit options; such as inches, cm, feet, meters, etc. When the user selects one the method NewUnits_Click is invoked. All works fine, however the contextmenu does not go away when the user selects a menu option. Pressing somewhere else on the screen like the application menu bar will then clear it. Has anyone else seen this problem, or see something wrong with the code below. It seems to have something to do with the ItemTemplate/DataTemplate I have, as creating an set of menu items by hand works fine.
<TextBlock Width="100" Text="{Binding CurrentUnits}" TextAlignment="Right">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu ItemsSource="{Binding Units}">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="NewUnits_Click" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
If you aren't using MVVM when working with WPF, I highly suggest you to start doing so. And even if you are not, I suggest that instead of binding to Click you instead bind on the Command property which wants something that implements ICommand. I think that the behavior you are getting is intended, buttons and menu items in WPF are intended to bind to Commands, it's not just WinForms 2.0.
Another solution would be to hide the Context menu in the code-behind. Perhaps this resource will help you in achieving that.
I have a small custom control which downloads & displays a contacts image. It ensures that only 1 image is being downloaded at a time by adding itself to a static queue of images awaiting to be downloaded.
It is possible to get quite long contact lists. So I want it to only enter the download queue when it actually becomes visible on the screen (there's a default image).
I've tried placing the logic in the Loaded event, overriding OnRender and the IsVisibleChanged event, but none seem to give me what I want.
any suggestions?
D.R
Edit:
This is a WPF Application, sorry for not mentioning before...
Some system controls (like ListView) have property "VirtualMode" if you set it to true and handle RetrieveVirtualItem event. This event invokes for items which are visible currently and you have to fill those items with data(images) you want. So that you don't need to fill all items at once.
Have you considered using a PriorityBinding in order to provide that functionality?
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<StackPanel Orientation="Horizontal">
<Image>
<Image.Source>
<PriorityBinding FallbackValue="{StaticResource ImgDownloading}">
<Binding Path="ImageSource" IsAsync="True" />
</PriorityBinding>
</Image.Source>
</Image>
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
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.