I need to be able to manually select ListBox items in Windows Phone 7. My ListBox contains the following style that uses a WrapPanel
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<[WP7Panels:WrapPanel][2] />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
</Style>
so that I can realize the wrapping effect.
When I did this in WPF, I subscribed to ListBox.ItemContainerGenerator.StatusChanged event. In the event handler for this event, I used:
var obj = ListBox.ItemContainerGenerator.ContainerForItem(objInstance)
as ListBoxItem; if (obj != null) {
obj.IsSelected= true; }
I tried doing the same thing for WP7 and noticed that:
ListBox.ItemContainerGenerator no longer exposes StatusChanged event. There is instead an ItemsChanged event, so I handled this instead.
However, when I call ItemContainerGenerator.ContainerForitem(objInstance) or even ItemContainerGenerator.ContainerFromIndex(), I get always get null.
Just to add, I have overriden GetHashCode and Equals on the class which is being presented in the list box.
Ideas please.
I'm not sure if this is the best way of doing this but here is how I would do it:
1) Create a class which inherits INotifyPropertyChanged interface (You can find how to use it easily via google). Wrap your information that you put into your ListBox in that class.
2) Create a IsSelected property for that class.
3) Bind IsSelected property of the class to the IsSelected property of ListBox's items.
4) Now you can just change IsSelected property of the class that you had created whenever you want and everything happens itself.
Related
I'm doing WP7 app using Panorama control and have a problem with binding into Panorama Title property. Is it possible to bind that value out from ViewModel object?
Binding in xaml file:
<controls:Panorama x:Name="prmPanorama" Title="{Binding Voyage.Title}">
Voyage property of ViewModel is a Model entity (with Title property inside) with OnNotifyPropertyChanged event fired every time it changes:
private Voyage _voyage;
public Voyage Voyage
{
get { return _voyage; }
set
{
if (_voyage != value)
{
_voyage = value;
OnNotifyPropertyChanged("Voyage");
}
}
}
When I bind the same property into another control, eg. TextBlock, binding works just fine:
<TextBlock Text="{Binding Voyage.Title}" />
The text shown in that text block is as it should be but on the same time panorama title is not binded right - it's collapsed.
Does anyone tried to do that kind of binding? I have no idea why it doesn't work.
<DataTemplate x:Key="TitleDataTemplate">
<TextBlock Text="{Binding}" />
</DataTemplate>
...
<controls:Panorama Title="{Binding Voyage.Title}"
TitleTemplate="{StaticResource TitleDataTemplate}">
The control template of the panorama control uses a content presenter to display whatever value the its title property has kind of like a button. When setting the title template property, you indirectly set the content template of the content presenter.
That is why you have to set the title property on the panorama control and then can use that value in your title template for binding. In other words its not enough to just bind to the title you have to give it a template.
Check out this link for more info
I have a control with white text foreground color and transparent background color.
Later on this usercontrol will be added into a different control that carries the real background color.
However during designing this, control due white foreground on white background in VS 2010, I can't obviously see anything. In there anyway to define a different color for just the design time?
I have tried this:
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
}
But this doesn't work. Any tips?
UPDATE:
I dont understand how this works for you guys. I have created a new Silverlight 4.0 Application and have inserted this line of code into the ctor:
public MainPage()
{
InitializeComponent();
LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
}
<UserControl x:Class="SilverlightApplication3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot">
</Grid>
</UserControl>
When I go into Designer, I still dont see it as blue. And I dont even have any isInDesignTime Condition there. What I am missing here?
Thanks,
Kave
Here's one way:
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Yellow);
}
If you switch to creating a templated control, you'll need to wait to set things up in OnApplyTemplate, like in this example:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Border b = this.GetTemplateChild("backBorder") as Border;
if (b != null && System.ComponentModel.DesignerProperties.IsInDesignTool)
{
b.Background = new SolidColorBrush(Colors.Orange);
}
}
Assuming this is the template:
<Style TargetType="local:TemplatedControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TemplatedControl1">
<Border x:Name="backBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also like to add conditional compile directives around code like this, as it's only for the developer/designer and is never needed at run-time.
#if DEBUG
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
LayoutRoot.Background = new SolidColorBrush(Colors.Yellow);
}
#endif
Note that this entire technique works only when the UserControl you're creating is used within* another* UserControl/Control at design time. So, if the code I suggested above is placed in a UserControl named UserControlWithDesignMode, then you must have another UserControl, UserControlHost, that contains an instance of the UserControlWithDesignMode control to see the behavior work at design time. The code-behind for the currently edited control does not execute when you're editing it. It only executes when it's contained within another host (in Silverlight, another UserControl for example).
One option would be to give the UserControl a background color, and then override that where you use it. So when you're editing the UserControl in isolation, it would have a background color; but when you're editing a control that contains that UserControl, you would see it with the transparent background like you want.
So the UserControl's XAML file would look like this:
<UserControl x:Class="MyUserControl" ... Background="DarkBlue">
And then in some other screen, where you use it, you could do:
<my:MyUserControl Background="Transparent" ...>
Inelegant, but simple.
Alternate approach that doesn't involve code:
Install the "Visual Studio 2012 Color Theme Editor" found here:
http://visualstudiogallery.msdn.microsoft.com/366ad100-0003-4c9a-81a8-337d4e7ace05
Or for VS 2010: http://visualstudiogallery.msdn.microsoft.com/20cd93a2-c435-4d00-a797-499f16402378
Create a new custom theme based on the one you want to modify.
Click the "Show All Elements" filter button in the upper-left of the theme editor
Type "artboard" in the search-box in the upper-right of the theme editor
Set the "Cider -> ArtboardBackground" color to a different color of your choice.
Yay! :D
Note: the "Cider -> ArtboardBackground" color theme field is found in VS2012 but I cannot confirm whether it has the same name in VS2010
You can use following code within UserControl constructor:
For WPF:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
LayoutRoot.Background = new SolidColorBrush( Colors.Blue );
}
For WPF / Silverlight:
if (DesignerProperties.GetIsInDesignMode( this ))
{
LayoutRoot.Background = new SolidColorBrush( Colors.Blue );
}
Good thread, especially when doing some MVVM the fact that UserControls appear on white when they are transparent is very annoying.
I'm sure you could do this in Blend with a state defined based on whether a designer is running, but I don't think that'd reduce the amount of work.
Still not sure how to avoid the code in the code behind and avoiding having to open blend, so if anybody has suggestions thanks in advance for posting.
I would suggest to use Opacity
<my:MyUserControl Background="Transparent" ...>
That doesn't work, since it will make any child controls inside the usercontrol invisible at run time.
One option would be to give the UserControl a background color, and then override that where you use it.
Did you try to set the Background on the UserControl? Not sure why but for me it doesn't work.
What does work is to set the Background of the Content, like so
<UserControl x:Class="...">
<StackPanel Background="{StaticResource PhoneChromeBrush}">
...
then putting the following code in the constructor of the view
public View() {
InitializeComponent();
var c = Content as Panel;
if (c != null) c.Background = new SolidColorBrush(Colors.Transparent);
}
Another technique mentioned in this SO question is to use the undocumented property d:DesignerProperties.DesignStyle, which works great for applying a design-time-only style to a single control, but which doesn't appear to work for a Style in a ResourceDictionary that would apply to all of the appropriately-typed controls or elements under the scope of the dictionary.
To solve this, on that same page I provide a simple solution for deploying a designer-only style into a ResourceDictionary. Here is a summary of that answer:
First, put the desired style in a XAML dictionary in the normal way.
<Window.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Resources>
Then, in the C# code, remove the style from the ResourceDictionary when design mode is not detected. Do this is in the OnInitialized override:
protected override void OnInitialized(EventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this) == false)
Resources.Remove(typeof(TreeViewItem));
base.OnInitialized(e);
}
Design Mode: Runtime Mode:
In the XAML template, I used a listbox to populate some dynamic data.
Now I want to disable touch selection in certain listbox items in Windows Phone 7. How to do that ?
I had done some little research, some people said that the selection event could be prevented in the listbox.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/8eeed347-ede5-4a24-88f1-953acd16e774
Hope some smart guys could teach me how to solve the problem.
Thanks.
You could use the ItemsControl instead of ListBox. ItemsControl is like ListBox, but it comes without the selection stuff.
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use ItemsControl wrapped in a ScrollViewer. This will give you the same effect without selection (and will allow you to scroll just like a ListBox does)
<ScrollViewer>
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Try using a variable to store the last selected index.
If the user then selects an item which you don't want to handle the selection event for then set the selected index back to the stored value. If you do want to hand;e the selection changed event then be sure to update your stored value too.
You'll probably also want to avoid triggering the selection changed when you manually set the index (back).
A simple boolean flag could be used to track this.
Old question but it doesn't look answered. You can do it through code, manipulating the selected item and index, but that's ugly and cumbersome. Instead let's do it declaratively (the XAML way!) with your bound items.
First you'll need a ViewModel with a list of items. Each item needs (at minimum) a property to display and a property to determine if the item is enabled or not.
Here's a sample viewmodel for a single item in the list:
class MyViewModel : ViewModelBase
{
private string _title;
public string Title
{
get { return _title; }
set
{
if(value == _title) return;
_title = value;
RaisePropertyChanged("Title");
}
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if(value == _isEnabled) return;
_isEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
}
The above example assumes MVVM Light for the ViewModelBase and the RaisePropertyChanged method, but you can do it with IPropertyNotified yourself (or any other MVVM library).
Next you'll have a listbox with markup similar to the following:
<ListBox ItemsSource="{Binding MyItems}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter IsHitTestVisible="{Binding IsEnabled}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
That's it. Now just load up some viewmodel with a list of items:
MainViewModel.MyItems = new ObservableCollection<MyViewModel>();
MainViewModel.MyItems.Add(new MyViewModel { Title = "Star Wars", IsEnabled = true });
MainViewModel.MyItems.Add(new MyViewModel { Title = "The Sound of Music", IsEnabled = false });
MainViewModel.MyItems.Add(new MyViewModel { Title = "Aliens", IsEnabled = true });
MainViewModel.MyItems.Add(new MyViewModel { Title = "Debbie Does Dallas", IsEnabled = false });
MainViewModel.MyItems.Add(new MyViewModel { Title = "True Grit", IsEnabled = false });
Only the sci-fi movies in this example are clickable.
Hope that helps.
The problem: We want to bind a HierarchicalDataTemplate’s ItemsSource property to a CollectionViewSource, to perform grouping and filtering.
The background: The original issue we were working on had to do with filtering a TreeView. Specifically, we found that using a CollectionViewSource to filter worked but caused the TreeView nodes to collapse. It's likely because the View’s Refresh function regenerates its list of objects which causes the TreeView to regenerate its nodes, causing the original nodes’ expansion states to be lost. We resolved this by writing a class that is similar to CollectionViewSource but preserves the View by editing the object list in place so that when it changes due to filtering, the associated TreeView nodes aren’t destroyed.
This has worked perfectly for us and we’d like to use it at deeper levels in our TreeView, bringing us back to our problem. Currently, we have a HierarchicalDataTemplate that looks like this:
<HierarchicalDataTemplate
x:Key="tableTemplate"
ItemsSource="{Binding Path=DataItems}"
ItemTemplateSelector="{StaticResource tableGroupsTemplateSelector}"
>
Instead, we want it to behave like this:
<HierarchicalDataTemplate
x:Key="tableTemplate"
ItemTemplateSelector="{StaticResource tableGroupsTemplateSelector}"
>
<HierarchicalDataTemplate.ItemsSource>
<Binding>
<Binding.Source>
<CollectionViewSource
Source="{Binding Path=DataItems}"
/>
</Binding.Source>
</Binding>
</HierarchicalDataTemplate.ItemsSource>
Unfortunately, this approach doesn’t seem to work. From what we can tell, the binding within the CVS never fires; no binding errors are raised; we tried attaching a converter and setting a breakpoint but the breakpoint was never hit. We’ve also tried various other solutions, including: using RelativeSource, moving the CollectionViewSource into the template’s Resources, and incorporating TreeViewItem’s into the template. However, nothing has worked.
As an aside, I do realize that a ViewModel approach would enable filtering. However, I'm at a place in our development cycle where I can't make that type of change so I'm looking for alternatives, like the CollectionViewSource approach.
Any help you can give would be appreciated.
Thanks,
-Craig
In my applications I have found that the simplest way to achieve tree view filtering is by binding the visibility to a property on the listed objects.
This approach may not work for you but if you would like to try it here is an example of what I have done.
In the Tree View Resources you add a Style Trigger. you can bind the trigger to any property on the objects that your displaying, and you can add in a value converter if you want to add some logic to inspect the item and decide if it should be displayed or not.
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Display}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
The Trigger will Collapse the treeView item if the "Display" property is false.
To avoid adding a "Display" property to your objects, you can do the following:
public class PositionVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int positions = (int) value;
if (positions > 0)
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotSupportedException();
}
}
And in the TreeView Style binding add the value converter:
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger Value="False">
<Binding Path="Positions">
<Binding.Converter>
<local:PositionVisibilityConverter/>
</Binding.Converter>
</Binding>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
You dont need to specify the path if you want to inspect the whole object.
This method seemed to work best for me, give it a try.
In a Prism v2 application, I define two regions, each a tabitem in a tabcontrol:
<UniformGrid Margin="10">
<TabControl>
<TabItem Header="First" Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>
<TabItem Header="Second" Name="SecondRegion" cal:RegionManager.RegionName="SecondRegion"/>
</TabControl>
</UniformGrid>
In the bootstrapper two modules are loaded and each injects a view into each of the tabitems:
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(SecondModule.SecondModule));
catalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
return catalog;
}
Now, of course, I want to perform the decoupling magic that I keep reading about and uncomment one of the modules and see its tab item not appear at all. Instead, on the contrary, there are still two TabItems and one is empty. This tells me that my application is still tightly coupling data and UI as in the bad old WinForm days.
So what do I need to do here to make this dynamic, so that the UI changes dynamically based on what modules are loaded, i.e. so that I could load 10 modules/views in my bootstrapper and there would automatically be 10 TabItems in the TabControl?
INTERMEDIATE ANSWER:
If I just make one region in a TabControl:
<TabControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>
and then load both controls into the MainRegion:
public void Initialize()
{
regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.SecondView));
}
...
public void Initialize()
{
regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.HelloWorldView));
}
then I get a TabControl with two tabs, each with a view in it, which is what I want.
But the TabItem headers are not defined. How do I dynamically define the header (e.g. not in the XAML but dynamically back in the View classes)?
This works too:
public class View : UserControl
{
public string ViewName { get; set; }
}
and then in the shell:
<Window.Resources>
<Style TargetType="{x:Type TabItem}" x:Key="TabItemRegionStyle">
<Setter Property="Header" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.ViewName}" />
</Style>
</Window.Resources>
...
<TabControl cal:RegionManager.RegionName="RightRegion" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" Grid.Column="2"
x:Name="RightRegion" ItemContainerStyle="{StaticResource TabItemRegionStyle}" />
Nice.
You can remove the ViwewName property on the view and change the binding on the TabItem value to be Value="{Binding DataContext.HeaderInfo}" ... where HeaderInfo is a property of your DataContext object - IE the business object which the Tab Item represents. This is a little more elegant.
You are on the right track with your modification.
The way I usually achieve the header is by adding an object to the region instead of a control, and datatemplating it with the control.
This object defines a property (let's say MyHeaderProperty) which I then use to bind to using an ItemContainerStyle on the TabControl.
I do not know if there is a way to achieve that without resorting to that kind of trick (an intermediate object and a DataTemplate).