Silverlight 4.0 Cannot create an instance of "CustomControl" - silverlight-4.0

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;
}
}
}

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>

How can I binding DependencyProperty in Style(UWP)?

I wanna make a custom usercontrol which actually just a image button.
Here is my project:
Here is code of ImageButton.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Controls;
namespace App1.UC
{
class ImageButton:Button
{
public ImageSource BackgroundImage
{
get { return (ImageSource)GetValue(BackgroundImageProperty); }
set { SetValue(BackgroundImageProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(ImageSource), typeof(ImageButton), null);
}
}
Here is code of ImageButton.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1.UC">
<Style TargetType="local:ImageButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border Background="{Binding local:BackgroundImage}"></Border>
<Border Background="Blue"></Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>
And here is code of MainPage.xaml:
<Page xmlns:my="using:App1.UC"
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/UC/ImageButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<my:ImageButton Height="100" Width="100" BackgroundImage="Assets/Wide310x150Logo.png" />
</Grid>
</Page>
The problem is I can't see the BackgroundImage which I set in my:ImageButton.
I doubted if is something wrong with binding the BackgroundImage in ImageButton.xaml.
I googled about it and found something like How to bind to attached property in UWP?
I tried as it said:
{Binding Path=(local:ImageButton.BackgroundImage), RelativeSource={RelativeSource Self}}
But no use again.
What's wrong with my programme?Would you please help me ?
You don't have to extend the Button just for displaying an image as its background. You can use ImageBrush directly like this
<Button>
<Button.Background>
<ImageBrush ImageSource="Assets/Wide310x150Logo.png" />
</Button.Background>
</Button>
Update
Extending an existing control with additional dependency properties can be simplified to the following steps.
1. Right click on your project, then go Add > New Item.... In the popup, select Templated Control and give it a
name ImageButton.cs.
[![enter image description here][1]][1]
Visual Studio will now automatically generate a new folder called
`Themes` with a `ResourceDictionary` file named `Generic.xaml`,
which is where the default style of your `ImageButton` goes.
[![enter image description here][2]][2]
Visual Studio will also generate an `ImageButton` class with the
following code.
public sealed class ImageButton : Control
{
public ImageButton()
{
this.DefaultStyleKey = typeof(ImageButton);
}
}
2. Change the base class to Button and add your own dependency property to the class.
public sealed class ImageButton : Button
public ImageSource BackgroundImage
{
get { return (ImageSource)GetValue(BackgroundImageProperty); }
set { SetValue(BackgroundImageProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(ImageSource), typeof(ImageButton), null);
3. Provide your own style and put it inside Generic.xaml. Note you
will need to use TemplateBinding to pass values from your
dependency properties to the binding targets.
<Style TargetType="local:ImageButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Stretch="UniformToFill" Source="{TemplateBinding BackgroundImage}"></Border>
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
That's it. Now, you can use it like below
<my:ImageButton Height="100" Width="100" BackgroundImage="Assets/Wide310x150Logo.png" />

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!

Windows Phone 8.1 conditional formatting

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

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!