Windows Phone 8.1 conditional formatting - xaml

I would like to display some data on a ListView. The data is getting from a HttpClient request, but it's not so important at the moment. In the ListView I use Stackpanel and in this there are the datas. I would like to format the Stackpanel and the Textblocks inside the Stackpanel depending to their values. I can do it, but only in a complex way.
Here is my C# code:
public class TransTypeToAlignConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var ttype = (int)value;
HorizontalAlignment align;
switch (ttype)
{
case 1:
case 3:
align = HorizontalAlignment.Right;
break;
case 2:
align = HorizontalAlignment.Left;
break;
case 6:
align = HorizontalAlignment.Stretch;
break;
default:
align = HorizontalAlignment.Stretch;
break;
}
return align;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And the XAML code:
<Page.Resources>
<local:TransTypeToAlignConverter x:Key="TransTypeToAlign" />
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView x:Name="myList" Height="Auto" Grid.Row="1">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,0,10,0">
<Rectangle Fill="DarkGray" Width="320" Margin="3,18,0,0" HorizontalAlignment="{Binding transactionType, Converter={StaticResource TransTypeToAlign}}"/>
<StackPanel Width="320" Margin="0,13,3,3" HorizontalAlignment="{Binding transactionType, Converter={StaticResource TransTypeToAlign}}" Background="Gray">
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
As I mentioned, this code is working. But I would like to format other things as well, not the Alignment only. For example I would like a dynamic Stackpanel width, not the fix 320px. For this I should write a code similar to the above. I think it's too much code for a similar thing, and it will be hard to modify later.
I have one value (TransType) which defines the alignment, the stackpanel width, the textblock message, and the color of the text. So, is it possible to make a template for formatting xaml?

A criticism of the pattern comes from MVVM creator John Gossman himself, who points out that the overhead in implementing MVVM is "overkill" for simple UI operations. MVVM Wiki
You can either do it with multiple converters or you can do it with a ItemTemplateSelector, in the latter case you would need to defined each template separately.
SO example of using ItemTemplateSelector
GridView ItemTemplateSelector

Related

How to customize ListViewItem properly with hideable content and bind it to a model, in UWP?

I would like to create a ListView where items have a "hover" and a "selected" state, displaying different content in each state.
There have been some similar question here on StackOverflow, but none helped in my particular case.
Let's say I have a model:
public class TagFile : BaseBind // A class with INotifyProperyChanged interface
{
private string path;
public String Path
{
get { return path; }
set
{
SetProperty(ref path, value);
}
}
public void SelectButtonClick()
{
// Do something
}
public void HoverButtonClick()
{
// Do something
}
}
...then I have a main ViewModel:
public class AppViewModel : BaseBind
{
public ObservableCollection<TagFile> ItemsList { get; set; }
// Other things
}
... then a Page:
<Grid>
<ListView ItemsSource="ItemsList">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TagFile">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind Path, mode=OneWay}"/>
<Button Content="Select Button" Click="{x:Bind SelectButtonClick}"/>
<Button Content="Hover Button" Click="{x:Bind HoverbuttonClick}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I would like to customize ListViewItem Style in order to make the SelectButton appear only when the ListViewItem is selected and the HoverButton only when the cursor is onto it.
I already know that I need to play with ItemTemplate and ItemContainerStyle, but it seems to be more difficult than I tought at the beginning, because I could create a custom style for the ItemContainerStyle in this way (using the IsSelected property from ListViewItem):
<Style TargetType="ListViewItem" x:Key="TestContainerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentPresenter x:Name="Presenter" Content="{TemplateBinding Content}"/>
<Button Grid.Row="1" Content="Select Button" Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
...but in this way:
1) I loose all the default brushes;
2) I don't know how I could bind the SelectButton to the model's command;
3) I would have to find a way for the "hover" state, in order to display the HoverButton;
4) Last, but not least... I would like to include all the customizations in the App.xaml file (or in associated ResourceDictionary files, as I need them across the entire app).
This is it... I have tried various pattern from StackOverflow answers, but my case includes many differnt things (compile binding in App.xaml files, create a "hover" state, using binding in a Style without a DataType, etc.) that are answered here only separately.
I need to... put them all together, and it seems more tricky than I thought, as I said.
Thank you for your help, really appreciate.
EDIT: the behaviour should be like the Groove App list items: some buttons (Play and Remove item) appears only when the the item is selected or in hover state.
Normal state:
...and Hover/selected state:
There are two ways where you can achieve what you want:
The first one: I don't prefer it as you will need to copy the whole style and embed your item template inside the style. It also doesn't scale if you want to have the same behavior for multiple parts of your app.
You can copy the ListViewItem style which uses UIElement tree and visual states instead of a ListViewItemPresenter. This way you don't lose default brushes.
Note: look in the above link for the template with x:Key="ListViewItemExpanded".
<!-- Style for Windows.UI.Xaml.Controls.ListViewItem -->
<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
Second Note:
These styles and resources are based on the Windows Software
Development Kit (SDK) for Windows 10, Version 1511 (Windows SDK
version 10.0.10586.0). Styles and resources from other versions of the
SDK might have different values. For design purposes, generic.xaml is
available in the (Program Files)\Windows
Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic
folder from a Windows SDK installation.
Now you can use pointerover and selected states to show and hide different elements.
Second approach: Using custom control [Recommended]
Break your problem to two things:
You can define IsSelected property in your model/viewmodel for each item, and update that within your select command. Now you can control the first button visibility by binding to IsSelected.
Second is the hover part, you create a custom control which expose a dependency property IsPointerOver and you can bind visibility of the 'Hover Button' to it.
Example:
public class ExtendedControl : ContentControl
{
public static readonly DependencyProperty IsPointerOverProperty =
DependencyProperty.Register("IsPointerOver", typeof(bool), typeof(ExtendedControl),
new PropertyMetadata(false));
public bool IsPointerOver
{
get => (bool)GetValue(IsPointerOverProperty);
protected set => SetValue(IsPointerOverProperty, value);
}
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
base.OnPointerEntered(e);
IsPointerOver = true;
}
protected override void OnPointerCanceled(PointerRoutedEventArgs e)
{
base.OnPointerCanceled(e);
IsPointerOver = false;
}
protected override void OnPointerExited(PointerRoutedEventArgs e)
{
base.OnPointerExited(e);
IsPointerOver = false;
}
}
Xaml:
<ListView.ItemTemplate>
<DataTemplate >
<local:ExtendedControl x:Name="ExtendedControl">
<StackPanel>
<Button Content="Always Visible"/>
<Button Content="Only on hover" Visibility="{x:Bind ExtendedControl.IsPointerOver,Mode=OneWay,Converter={StaticResource BoolToVisibilityConverter}}"/>
</StackPanel>
</local:ExtendedControl>
</DataTemplate>
</ListView.ItemTemplate>

MasterDetail ListView and editable ContentPresenter: what is wrong?

I'm based on the official Microsoft sample to create a MasterDetail ListView:
MasterDetail ListView UWP sample
I have adapted it to my case, as I want that users can edit directly selected items from the ListView. But I meet a strange comportement:
when I add a new item to the ListView, the changes of the current item, done in the details container, are well saved
but when I select an existing item in the ListView, the changes of the current item, done in the details container, are not saved
Here is a screenshot of my app:
The XAML of my ListView is like this:
<!-- Master : List of Feedbacks -->
<ListView
x:Name="MasterListViewFeedbacks"
Grid.Row="1"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource MasterListViewFeedbacksItemTemplate}"
IsItemClickEnabled="True"
ItemsSource="{Binding CarForm.feedback_comments}"
SelectedItem="{Binding SelectedFeedback, Mode=TwoWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.FooterTemplate>
<DataTemplate>
<CommandBar Background="White">
<CommandBar.Content>
<StackPanel Orientation="Horizontal">
<AppBarButton Icon="Add" Label="Add Feedback"
Command="{Binding AddItemFeedbacksCommand}" />
<AppBarButton Icon="Delete" Label="Delete Feedback"
Command="{Binding RemoveItemFeedbacksCommand}" />
</StackPanel>
</CommandBar.Content>
</CommandBar>
</DataTemplate>
</ListView.FooterTemplate>
</ListView>
The XAML of the ListView's ItemTemplate is:
<DataTemplate x:Key="MasterListViewFeedbacksItemTemplate" x:DataType="models:Feedback_Comments">
<StackPanel Margin="0,11,0,13"
Orientation="Horizontal">
<TextBlock Text="{x:Bind creator }"
Style="{ThemeResource BaseTextBlockStyle}" />
<TextBlock Text=" - " />
<TextBlock Text="{x:Bind comment_date }"
Margin="12,1,0,0" />
</StackPanel>
</DataTemplate>
The XAML of the Details container is like this:
<!-- Detail : Selected Feedback -->
<ContentPresenter
x:Name="DetailFeedbackContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListViewFeedbacks.SelectedItem, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="models:Feedback_Comments">
<StackPanel Visibility="{Binding FeedbacksCnt, Converter={StaticResource CountToVisibilityConverter}}">
<TextBox Text="{Binding creator, Mode=TwoWay}" />
<DatePicker Date="{Binding comment_date, Converter={StaticResource DateTimeToDateTimeOffsetConverter}, Mode=TwoWay}"/>
<TextBox TextWrapping="Wrap" AcceptsReturn="True" IsSpellCheckEnabled="True"
Text="{Binding comment, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.ContentTransitions>
<!-- Empty by default. See MasterListView_ItemClick -->
<TransitionCollection />
</ContentPresenter.ContentTransitions>
</ContentPresenter>
The "CarForm" is the main object of my ViewModel. Each CarForm contains a List of "Feedback_Comments".
So in my ViewModel, I do this when I add a new comment:
private void AddItemFeedbacks()
{
FeedbacksCnt++;
CarForm.feedback_comments.Add(new Feedback_Comments()
{
sequence = FeedbacksCnt,
creator_id = user_id,
_creator = username,
comment_date = DateTime.Now
});
SelectedFeedback = CarForm.feedback_comments[CarForm.feedback_comments.Count - 1];
}
=> the changes done in the Feedback_Comment that was edited before the add are well preserved
I don't do anything when the user select an existing Feedback_Comment: this is managed by the XAML directly.
=> the changes done in the Feedback_Comment that was edited before to select anoter one are not preserved
=> Would you have any explanation?
The TwoWay binding for the Text property is updated only when the TextBox loses focus. However, when you select a different item in the list, the contents of the TextBox are no longer bound to the original item and so are not updated.
To trigger the update each time the Text contents change, so that the changes are reflected immediately, set the UpdateSourceTrigger set to PropertyChanged:
<TextBox Text="{Binding comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Triggering changes everywhere
To ensure your changes are relflected everywhere including the list, you will need to do two things.
First, your feedback_comments is of type ObservableCollection<Feedback_Comments>. This ensures that the added and removed items are added and removed from the ListView.
Second, the Feedback_Comments class must implement the INotifyPropertyChanged interface. This interface is required to let the user interface know about changes in the data-bound object properties.
Implementing this interface is fairly straightforward and is described for example on MSDN.
The quick solution looks like this:
public class Feedback_Comments : INotifyPropertyChanged
{
// your code
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged( [ CallerMemberName ]string propertyName = "" )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
Now from each of your property setters call OnPropertyChanged(); after setting the value:
private string _comment = "";
public string Comment
{
get
{
return _comment;
}
set
{
_comment = value;
OnPropertyChanged();
}
}
Note, that the [CallerMemberName] attribute tells the compiler to replace the parameter by the name of the caller - in this case the name of the property, which is exactly what you need.
Also note, that you can't use simple auto-properties in this case (because you need to call the OnPropertyChanged method.
Bonus
Finally as a small recommendation, I see you are using C++-like naming conventions, which does not fit too well into the C# world. Take a look at the recommended C# naming conventions to improve the code readability :-) .

Universal Apps: How to bind a property of a ListViewItem (container) to the actual item (View Model)?

I have this ListView
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding IsValid, Converter={StaticResource BooleanToBrushConverter}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I know bindings don't work for Setters in Universal Applications but, then how do I bind a the container of an item with the item itself? What's the point of creating a custom container if you cannot provide any logic, but constant values?
You need to be careful with backgrounds in the UWP ListViewItem, as there is a lot of complex theming around this including different kinds of pressed and drag backgrounds
I think an easier way to achieve this is to change the content alignments in the ListViewItem, and then add a grid to your item template from which you can add your background
<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{Binding Path=IsValid, Converter={StaticResource BooleanToBrushConverter}}">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
OK, you can't bind to the DataContext using the normal ways, but you can do it using other (smart) methods:
See this post. It provides a "helper" that allows to bind to the DataContext with a Setter.
This is pretty easy, really.
I would guess you are trying to use the variable sized grid view? That's a pretty common request, actually. But what you are asking for is tricky because of the various scopes and how things are rendered.
The first think you will need to do is override ListView with your own custom ListView. Let's call it MyListView. Like this:
public class MyItem
{
public int RowSpan { get; set; }
public int ColSpan { get; set; }
}
public class MyListView : ListView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
var model = item as MyItem;
try
{
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, model.ColSpan);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, model.RowSpan);
}
catch
{
element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
}
finally
{
element.SetValue(VerticalContentAlignmentProperty, VerticalAlignment.Stretch);
element.SetValue(HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch);
base.PrepareContainerForItemOverride(element, item);
}
}
}
Everything takes place in PrepareContainerForItemOverride and it's the only method you need override in the subclass. Please also notice that I have not set them to a binding. This is because these properties are only observed when the item is rendered. If you want to refresh your ListView and re-render your items based on new values, you need to call InvalidateMeasure() on the root panel, which is tricky. You can do it like this:
// MyListView
public void Update()
{
if (!(this.ItemsPanelRoot is VariableSizedWrapGrid))
throw new ArgumentException("ItemsPanel is not VariableSizedWrapGrid");
foreach (var container in this.ItemsPanelRoot.Children.Cast<GridViewItem>())
{
var model = item as MyItem;
VariableSizedWrapGrid.SetRowSpan(container, data.RowSpan);
VariableSizedWrapGrid.SetColumnSpan(container, data.ColSpan);
}
this.ItemsPanelRoot.InvalidateMeasure();
}
If this makes sense, you can see the entire implementation here.
Best of luck!

XAML how to make a textblock/text box edit toggle?

I may be approaching this all wrong, so tell me if you have an alternative suggestions.
I'm making an app for windows RT that will have a bunch of text blocks displayed to the user, for example character stats.
The user will see:
Str: 10
Con: 10
Dex: 10
and so on.
I want them to be able to fill these in, then have a select view values calculated based on the result.
My though was to click an "Edit" button at the top and toggle some text boxes over each editable text block.
When trying to set this up using "Blend for Visual Studio" I can't seem to make a text box that is smaller than 49x34 (much larger than my text blocks).
I was going to find a way to generate a text box for each text block (using its dimensions) on button click, but since they will always be the same and there will be a lot of them I was trying to make them static via blend.
I'm pretty new to XAML, and I can't seem to find a good example of people setting up editable fields like this, so how should I make a bunch of static fields have editable text boxes?
I would create both the TextBox and TextBlock overlays in XAML, and place them directly on top of each other in a Grid, using Horizontal and Vertical alignments to "Center" to ensure that the text is always completely lined up. I would also use static Widths to ensure that the columns line up well.
From there, you can directly bind the Visibility to some boolean "IsEditing" property, to make sure that only one of the controls are shown at a time.
<StackPanel Orientation="Horizontal">
<TextBlock Text="Str: " Width="40" VerticalAlignment="Center" />
<Grid Width="40" VerticalAlignment="Center">
<TextBlock Text="{Binding Strength}"
Visibility="{Binding IsEditing, Converter={StaticResource BooleanToInvisibilityConverter}}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding Strength}"
Visibility="{Binding IsEditing, Converter={StaticResource BooleanToVisibilityConverter}}"
VerticalAlignment="Center"
HorizontalContentAlignment="Center" />
</Grid>
</StackPanel>
Somewhere along the way you'll have to define your "BooleanToVisibility" and "BooleanToInvisiblity" converter resources. I like this implementation by Diedrik Krols. It's nice and simple, with the option to invert.
You might want to use a style for a TextBox, which changes depending on whether or not the "IsReadOnly" property is true or not.
When IsReadOnly is true, you can set the BorderBrush and Background to Transparent, thus making it look like a normal textblock.
In this way, you don't have to overlay TextBlocks and TextBoxes; just use TextBox controls by themselves, and toggle the "IsReadOnly" property when you click the Edit button.
In your resources:
<Style x:Key="MyEditableField" TargetType={x:Type TextBox}>
<Style.Triggers>
<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Background" Value="Transparent" />
</DataTrigger>
</Style.Triggers>
</Style>
And here's one of your editable fields:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Str: " />
<TextBox Style="{StaticResource MyEditableField}"
Text="{Binding Strength}"
IsReadOnly="{Binding IsEditingDisabled}" />
</StackPanel>
Late answer, but who wants can also create a custom editable textbox, its pretty easy actually here is the code (obviously you can modify it for your own needs)
public class EditableTextBox : TextBox
{
public EditableTextBox()
{
this.BorderBrush = new SolidColorBrush(Colors.Black);
}
protected override void OnTapped(TappedRoutedEventArgs e)
{
this.IsReadOnly = false;
SetEditingStyle();
base.OnTapped(e);
}
protected override void OnDoubleTapped(DoubleTappedRoutedEventArgs e)
{
this.IsReadOnly = false;
SetEditingStyle();
base.OnDoubleTapped(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
this.IsReadOnly = true;
SetReadonlyStyle();
base.OnLostFocus(e);
}
public void SetReadonlyStyle()
{
this.BorderBrush.Opacity = 0;
this.Background.Opacity = 0;
}
public void SetEditingStyle()
{
this.BorderBrush.Opacity = 1;
this.Background.Opacity = 1;
}
}
Sample:
Tutorial: Full tutorial url
Using a property to toggle edit mode between view AND viewmodel is a bad design approach you should use events and command binding to communicate changes of states like this between view and viewmodel.
Here is an article that describes the principle in an MVVM compliant way:
http://www.codeproject.com/Articles/802385/A-WPF-MVVM-In-Place-Edit-TextBox-Control
Please have a look and tell me what you think.
This builds off of BTownTKD's solution, but as I really do prefer as much WPF of a solution as possible here is a bit of a modification, in my case I'm trying to modify the name of a tab control.
My view model has the following code:
private bool _isEditingName = false;
public bool IsEditingName
{
get
{
return _isEditingName;
}
set
{
_isEditingName = value;
OnPropertyChanged();
}
}
public ICommand StartEditing
{
get
{
return new DelegateCommand(() =>
{
IsEditingName = true;
});
}
}
public ICommand EndEditing
{
get
{
return new DelegateCommand(() =>
{
IsEditingName = false;
});
}
}
Next is my view that has the data template for the tab (not the content just the tab):
<TabControl ItemsSource="{Binding Items}" SelectedItem="{Binding ActiveItem}">
<TabControl.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center">
<TextBlock x:Name="TabName" Text="{Binding Name}" Visibility="{Binding IsEditingName, Converter={StaticResource InvertedBoolToVisConverter}}" VerticalAlignment="Center" HorizontalAlignment="Stretch" TextAlignment="Left">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding StartEditing}" />
</TextBlock.InputBindings>
</TextBlock>
<TextBox Text="{Binding Name}" Visibility="{Binding IsEditingName, Converter={StaticResource BoolToVisConverter}}" VerticalAlignment="Center" HorizontalContentAlignment="Stretch" TextAlignment="Left" IsVisibleChanged="TextBox_IsVisibleChanged">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding EndEditing}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding EndEditing}" />
</TextBox.InputBindings>
</TextBox>
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And last but not least, I wanted a double click to put me in edit mode, and to auto focus on the textbox and select all of the content for immediate typing. None of the xaml solutions were as clean as a simple code behind so I finally just decided on adding this to the textbox on visibility changed handler:
private void TextBox_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
var box = sender as TextBox;
if (box != null)
{
if ((bool)e.NewValue)
{
box.Focus();
box.SelectAll();
}
}
}
Out of all of the solutions I found, this was by far my favorite. Thanks everyone for your posts!! Helped me find a really good overall solution to my problem!

Silverlight 4.0 Cannot create an instance of "CustomControl"

I'm currently working on a small project, and I am having some trouble with silverlight custom controls. I am trying to create a custom ScrollViewer (that will contain small images) for photo gallery. The problem I have, is that Visual Studio is throwing the following error:
Cannot create an instance of ScrollableImageViewer
Could someone provide some pointers as to what might be the cause of this error, and maybe some ways to fix it?
Below, is the XAML and C# code that I wrote.
<navigation:Page x:Class="PWS.Views.Biography"
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"
xmlns:local="clr-namespace:PWS.Controls"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="This is the biography page">
<Grid x:Name="LayoutRoot">
<TextBlock x:Name ="tempText" FontSize="15" Foreground="Blue">this is the biography page</TextBlock>
<local:ScrollableImageViewer x:Name= "scrollableViewer" />
</Grid>
</navigation:Page>
My custom control class is called ScrollableImageViewer and below you can see the code for it.
namespace PWS.Controls
{
[ContentProperty("Items")]
public class ScrollableImageViewer : Control
{
public static DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached("Items", typeof(IList<UIElement>), typeof(ScrollableImageViewer), new PropertyMetadata(""));
public ScrollableImageViewer()
{
this.DefaultStyleKey = typeof(ScrollableImageViewer);
this.Items = new List<UIElement>();
}
public IList<UIElement> Items
{
get { return (IList<UIElement>)GetValue(ScrollableImageViewer.ItemsProperty); }
set { SetValue(ScrollableImageViewer.ItemsProperty, value); }
}
}
}
At the moment it is pretty simple and does not contain the custom logic for scrolling.
One more thing that I have to add is the generic.xaml file that holds the template for this control.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PWS.Controls"
>
<Style TargetType="local:ScrollableImageViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ScrollableImageViewer">
<Grid Background="{TemplateBinding Background}">
<ScrollViewer x:Name="internalScrollViewer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Hidden"
>
<StackPanel Background="{TemplateBinding Background}" FlowDirection="LeftToRight" IsHitTestVisible="False" Children="{TemplateBinding Items}"></StackPanel>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
As you can see from the template, I want my control to contain an internal ScrollViewer and a StackPanel whos Children property should be bound to the Items property of the custom control.
P.S I did some search on SO and found some questions regarding similar problems. Most of them were solved by adding code to the constructor to check if the control was displayed in the designer mode, but this did not work for me. It seems that I have a different problem.
P.P.S I'm just starting to learn Silverlight, so if you see some bad code please feel free to point it out to me.
when working with custom controls you have to deal with a lot of rules. For start lets assure this:
Your project must have a "themes" folder and inside it a "Generic.xaml" resource dictionary that holds the style with your template.
When defining a dependency property you cannot initialize it with values from different types. Your Items property is getting initialized with an empty string (""), this is the initial value for your property inside the "PropertyMetadata". A "null" or a "new ..." should do the trick.
Stated that, I offer you a work around to the Items property: give your StackPanel a name, access it from the "OnApplyTemplate" method. Use a temporal StackPanel to hold possible children that might be added before the real template is applied to your control and, when this really happens, pass the children from the temporal StackPanel to that in the template. For this to work use a NamedPart for your StackPanel.
Here is the code xaml:
<Style TargetType="local:ScrollableImageViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ScrollableImageViewer">
<Grid Background="{TemplateBinding Background}">
<ScrollViewer x:Name="internalScrollViewer"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Hidden">
<StackPanel x:Name="myStackPanel"
Background="{TemplateBinding Background}"
FlowDirection="LeftToRight"
IsHitTestVisible="False"></StackPanel>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is code behind:
[TemplatePart(Name = "myStackPanel", Type = typeof(StackPanel))]
[ContentProperty("Items")]
public class ScrollableImageViewer : Control
{
public ScrollableImageViewer()
{
this.DefaultStyleKey = typeof(ScrollableImageViewer);
}
StackPanel MyStackPanel = null;
StackPanel TemporalSP = new StackPanel();
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
MyStackPanel = GetTemplateChild("myStackPanel") as StackPanel;
// Pass created children before appliying the template.
for (int i = TemporalSP.Children.Count - 1; i >= 0; i--)
{
UIElement OneControl = TemporalSP.Children[i];
TemporalSP.Children.Remove(OneControl);
MyStackPanel.Children.Add(OneControl);
}
TemporalSP = null;
}
public UIElementCollection Items
{
get
{
if (MyStackPanel != null)
return MyStackPanel.Children;
else
return TemporalSP.Children;
}
}
}