WPF custom control not working as expected in Listview - wpf

I want to put an editabletextblock cutom control in a Listview datatemplate. I'm following this article and it works well.
But when i'm put this control in a Listview datatemplate, on double click on the Textblock, the event OnMouseDoubleClick of the custom control is fired but the Textbox is never display.
My Datatemplate :
<DataTemplate x:Key="ItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Column="0">
<Image Source="{Binding Icon}"
Margin="0 0 4 0" />
<localp:EditableTextBlock Text="{Binding Tag, Mode=TwoWay}"
VerticalAlignment="Center" />
</StackPanel>
</Grid>
<ListView
ItemTemplate={StaticResource ItemTemplate}
.... />
And i don't know why the OnMouseDoubleClick EditableTextBlock is fired but the inner Textbox is never displayed as expected.
Thanks is advance for your help,
Regards

Change the default values of TextBlockForegroundColorProperty and TextBoxForegroundColorProperty from null to something else:
public static readonly DependencyProperty TextBlockForegroundColorProperty =
DependencyProperty.Register("TextBlockForegroundColor",
typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(Brushes.Black));
public static readonly DependencyProperty TextBoxForegroundColorProperty =
DependencyProperty.Register("TextBoxForegroundColor",
typeof(Brush), typeof(EditableTextBlock), new UIPropertyMetadata(Brushes.Black));
Or set them in you Xaml:
<local:EditableTextBlock TextBlockForegroundColor="Black" TextBoxForegroundColor="Black" ... />
Edit
you can set keyboard focus to the TextBox, however, you should set e.Handled to true, or the OnTextBoxLostFocus will execute and hides your TextBox.
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
this.m_TextBlockDisplayText.Visibility = Visibility.Hidden;
this.m_TextBoxEditText.Visibility = Visibility.Visible;
if (m_TextBoxEditText.IsKeyboardFocusWithin ==false)
{
Keyboard.Focus(m_TextBoxEditText);
e.Handled = true;
m_TextBoxEditText.CaretIndex = m_TextBoxEditText.Text.Length;
}
}

Related

A TwoWay or OneWayToSource binding cannot work on the read-only property for same name property from different page [duplicate]

This question already has answers here:
InvalidOperationException - A TwoWay or OneWayToSource binding cannot work on the read-only property
(4 answers)
A TwoWay or OneWayToSource binding cannot work on the read-only property
(1 answer)
Closed 3 months ago.
I have two pages TunerPage.xaml and FormCameraSetting.xaml, and FormCameraSetting in TunerPage as Frame through binding, like this:
TunerPage.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<UniformGrid Grid.Column="0" Rows="1" Columns="2">
<Label Content="Shutter" FontFamily="Arial" Foreground="White" VerticalContentAlignment="Center"/>
<TextBox Style="{StaticResource TextBoxStyle1}" Text="{Binding Shutter, Mode=OneWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsEnabled ="False"/>
</UniformGrid>
<Frame Grid.Column="1" Content="{Binding PageDisplay}" NavigationUIVisibility="Hidden" Loaded="Frame_Loaded" Unloaded="Frame_Unloaded"/>
</Grid>
FormCameraSetting.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Shutter" FontFamily="Arial" Foreground="White" VerticalContentAlignment="Center"/>
<TextBox HorizontalContentAlignment="Right" Style="{StaticResource TextBoxStyleForm2}" Text="{Binding Shutter, UpdateSourceTrigger=Explicit}"/>
</Grid>
Both of them have TextBox. One in TunerPage.xaml is binding Shutter on TunerAction.cs and mode is Oneway. Another in FormCameraSetting.xaml is binding Shutter on CameraSettingViewModel.cs and mode is Twoway.
TunerAction.cs (ViewModel)
public double Shutter
{
get
{
return 2000;
}
}
CameraSettingViewModel.cs (ViewModel)
private double shutter;
public double Shutter
{
get
{
return shutter;
}
set
{
shutter = value;
}
}
Now in xaml.cs, I set their DataContext equal to their ViewModel when the page loaded.
TunerPage.xaml.cs
public TunerAction tunerAction = new TunerAction();
public TunerPage()
{
InitializeComponent();
}
private void SettingPageBase_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = tunerAction;
}
CameraSettingViewModel.xaml.cs
public CameraSettingViewModel viewModel = new CameraSettingViewModel();
public FormCameraSetting()
{
InitializeComponent();
}
private void CameraSettingTemplate_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = viewModel;
}
When I run VisualStudio, it shows an error:
But, if I change "Shutter" to another name in TunerAction.cs, or set DataContext after InitializeComponent() not when the page loaded, it will not cause this error.
Why did this error happen?
Thanks!

Bind header and content dynamically to expander

Firstly I see the type for Header and content for the expander, it says the type is object. I have a user control with name CommonExpanderUserControl as follows,
xaml:
<uwpControls:Expander Header="{Binding HeaderContent}" Content="{Binding MainContent}">
</uwpControls:Expander>
In xaml.cs (DataContext is set to this)
public static readonly DependencyProperty HeaderContentProperty =
DependencyProperty.Register("HeaderContent", typeof(object), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public object HeaderContent
{
get { return (object)GetValue(HeaderContentProperty); }
set { SetValue(HeaderContentProperty, value); }
}
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(ContentControl), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public ContentControl MainContent
{
get { return (ContentControl)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
Now I am using this UserControl somewhere outside as follows,
<UserControl.Resources>
<ContentControl x:Key="Header">
<Grid x:Name="ExpanderHeaderGrid" HorizontalAlignment="Stretch" Padding="0" Margin="0"
Background="{Binding LisSharedSettings.ChangeHeaderColor,Converter={StaticResource BoolToSolidBrushConverter}}">
<TextBlock x:Name="TextBlockLisSharedSettingsTitle"
x:Uid="/Application.GlobalizationLibrary/Resources/InstrumentSettingsViewLisSettingsTextBlockTitle"
Style="{StaticResource TextBlockStyleSectionHeader}"/>
</Grid>
</ContentControl>
<ContentControl x:Key="Body">
Some content here.
</ContentControl>
</UserControl.Resources>
<Grid>
<local:CommonExpanderUserControl HeaderContent="{StaticResource Header}" MainContent="{StaticResource Body}"/>
</Grid>
Binding Content control like that simply doesn't work. If I remove the MainContent binding and bind only the Header, it says object reference not set to an instance of an object. Please help.
The problem occurs StaticResource binding, we could not bind header with control by StaticResource. And for the scenario the better way is bind HeaderTemplate and send the data source to the header property like the following.
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<Grid
x:Name="ExpanderHeaderGrid"
Margin="0"
Padding="0"
HorizontalAlignment="Stretch"
Background="Red"
>
<TextBlock x:Name="TextBlockLisSharedSettingsTitle" Text="{Binding}" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<uwpControls:Expander Header="hello" HeaderTemplate="{StaticResource HeaderTemplate}" />
</Grid>

Set focus to 1st textbox in items control

Let's say I have an items control that is bound to a list of items on the VM. Inside the datatemplate is a textbox. How would I set focus to the first textbox in either XAML or the VM?
Thanks in advance for any help!
<ItemsControl ItemsSource="{Binding UsageItems}" Grid.Row="1" Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Month}" Style="{StaticResource Local_MonthLabel}" />
<core:NumericTextBox Value="{Binding Actual, Mode=OneWay}" Style="{StaticResource Local_ActualUsageEntry}" Grid.Column="2"/>
<core:ValidationControl Instance="{Binding Model}" Grid.Column="4" PropertyName="{Binding MonthNumber, StringFormat=AdjustedUsage{0}}">
<core:NumericTextBox Value="{Binding Adjusted}" DefaultValueIfNull="0" Style="{StaticResource Local_AdjustUsageEntry}" x:Name="AdjustmentEntry" inventoryLocationSetup:InitialFocusBehavior.Focus="True" />
</core:ValidationControl>
<telerik:RadComboBox ItemsSource="{Binding Converter={StaticResource Converter_EnumToEnumMemberViewModel}, Mode=OneTime, Source={x:Type Enums:UsageAdjustmentTypes}}" SelectedValue="{Binding Code, Mode=TwoWay}" Grid.Column="6" Style="{StaticResource Local_CodeSelector}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
I use an attached behaviour:
public static class InitialFocusBehavior
{
public static bool GetFocus(DependencyObject element)
{
return (bool)element.GetValue(FocusProperty);
}
public static void SetFocus(DependencyObject element, bool value)
{
element.SetValue(FocusProperty, value);
}
public static readonly DependencyProperty FocusProperty =
DependencyProperty.RegisterAttached(
"Focus",
typeof(bool),
typeof(InitialFocusBehavior),
new UIPropertyMetadata(false, OnElementFocused));
static void OnElementFocused(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = depObj as FrameworkElement;
if (element == null)
return;
element.Focus();
}
}
Then in the XAML bind it to True for the element you want focused:
<TextBox Width="200" Height="20" local:InitialFocusBehavior.Focus="True" />
=== UPDATE ===
Sorry, the code above just shows how to use a behaviour to give focus to a control on the page, if you want to do it to an element in the first item in an ItemControl then you'll have to instead apply the behavior to the ItemsControl itself and then in your update handler find the child by doing something like this instead:
static void OnElementFocused(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
ItemsControl itemsControl = depObj as ItemsControl;
if (itemsControl == null)
return;
itemsControl.Loaded += (object sender, RoutedEventArgs args) =>
{
// get the content presented for the first listbox element
var contentPresenter = (ContentPresenter)itemsControl.ItemContainerGenerator.ContainerFromIndex(0);
// get the textbox and give it focus
var textbox = contentPresenter.ContentTemplate.FindName("myTextBox", contentPresenter) as TextBox;
textbox.Focus();
};
}
You'll notice that I'm setting the focus inside the OnLoaded handler because I'm assuming that the items won't have been attached yet when the control is first created.
Also you've probably already figured it out by "local" is just the namespace that the InitialFocusBehavior class is defined in, you'll need to add something like this at the top of the xaml:
xmlns:local="clr-namespace:YourProjectNamespace"

Custom TabItem header with text and image - not sure how to proceeed

I have a TabControl in my XAML which is just an empty control as the TabItems are dynamically generated at runtime.
My problem is I want to have the tab title and an image (a "settings" image) in the Header but I'm not sure how to go about this. As I said, I'm generating the TabItems on the fly so how and where would a template to do this fit in and where would I put it etc? Would a TabItem header template apply to TabItem controls created dynamically? (I am assuming/hoping so!)
I've googled and searched around here but no-one quite seems to be doing what I'm doing... just wondering if someone could give me some guidance.
<Grid Name="MainGrid" Background="#333333" ShowGridLines="False" >
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" ToolTip="Settings">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="RoboNews" Foreground="SkyBlue" FontSize="32" Padding="5"/>
<Button Name="btnSettings" Background="Transparent" Grid.Column="1" BorderBrush="#333333" BorderThickness="0" HorizontalAlignment="Right"
Click="btnSettings_Click" ToolTip="Click for menu">
<!--<Image Source="Images/Settings48x48.png"/>-->
<Image Source="/Images/MenuOpen.png" Width="36" />
</Button>
</Grid>
<TabControl Name="tabCategories" Grid.Row="1" Background="Black" SelectionChanged="tabCategories_SelectionChanged">
</TabControl>
</Grid>
You can create a UserControl for your header
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}" x:Class="TabControlHeader.TabItemHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Width="Auto" Height="Auto">
<Grid >
<StackPanel>
<Image Source="{Binding ImageSource}"></Image>
<TextBlock Text="{Binding Text}"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
With ideally DPs defined in its ViewModel, but for the time being, in code behind:
public string ImageSource
{
get { return (string)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(string), typeof(TabItemHeader));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TabItemHeader));
Then while creating your TabItem you need to set its HeaderTemplate:
TabItem tabItem = new TabItem();
tabItem.Height = 100;
tabItem.Width = 50;
var header = new FrameworkElementFactory(typeof(TabItemHeader));
header.SetValue(TabItemHeader.TextProperty, "This is Text");
header.SetValue(TabItemHeader.ImageSourceProperty, "This is Image uri");
header.SetValue(TabItemHeader.HeightProperty, (double)50);
header.SetValue(TabItemHeader.WidthProperty, (double)50);
tabItem.HeaderTemplate = new DataTemplate { VisualTree = header };
tabCategories.Items.Add(tabItem);

Show and focus TextBox in DataTemplate

I have searched high and low, but I can't figure this one out. I am building a ListBox that has editable items. I have a DataTemplate for the ListBox.ItemTemplate that contains (among other things) a TextBlock and a TextBox. The TextBlock is always visible, and the TextBox is only visible after the user double-clicks on the TextBlock. When the user clicks another item in the list, the TextBox hides again to show the TextBlock. All of this works great. See my code:
XAML
<Window.Resources>
<local:GoalCollection x:Key="goals"/>
<DataTemplate x:Key="GoalItemTemplate" DataType="local:Goal">
<Grid>
<TextBlock Text="{Binding Title}"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"
VerticalAlignment="Center"/>
<TextBox Name="EntryBox"
Text="{Binding Title}"
Visibility="Hidden"
BorderBrush="{x:Null}"
Padding="-2,0,0,0"
Panel.ZIndex="1"
Margin="-2,0,0,0"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ListBox Name="GoalsList"
ItemsSource="{Binding Source={StaticResource goals}}"
HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource GoalItemTemplate}"
SelectionChanged="GoalsList_SelectionChanged" />
</Grid>
C#
public partial class MainWindow : Window
{
GoalCollection goals;
public MainWindow()
{
InitializeComponent();
}
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject { ... }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
goals = (GoalCollection)Resources["goals"];
}
private void TextBlock_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
TextBlock tblk = sender as TextBlock;
if (tblk == null)
return;
TextBox tbx = ((Grid)tblk.Parent).FindName("EntryBox") as TextBox;
if (tbx == null)
return;
tbx.Visibility = Visibility.Visible;
Keyboard.Focus(tbx);
}
}
private void GoalsList_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
ListBoxItem lbi;
ContentPresenter cp;
DataTemplate dt;
TextBox tbx;
foreach (Goal item in e.RemovedItems)
{
lbi = (ListBoxItem)GoalsList.ItemContainerGenerator.
ContainerFromItem(item);
cp = FindVisualChild<ContentPresenter>(lbi);
dt = cp.ContentTemplate;
tbx = (TextBox)dt.FindName("EntryBox", cp);
if (tbx == null)
continue;
tbx.Visibility = Visibility.Hidden;
}
}
}
The problem that I'm having is that the TextBox immediately shifts focus back to the host ListBoxItem after the double-click. An additional (third) click is required to focus on the TextBox.
Tracing through this, I have found that the TextBox does indeed receive focus. But then it immediately loses it (try adding a handler for the TextBox.LostKeyboardFocus event and step through and out of the `TextBlock_MouseLeftButtonDown()' method). Any ideas?
Thanks.
My guess is that the click event is bubbling up to the ListBox and it's handling it by selecting the item.
Try adding this to your Click event handler (after Keyboard.Focus(tbx);)
e.Handled = true;
If you want to give focus to a child element, try the FocusManager.
<DataTemplate x:Key="MyDataTemplate" DataType="ListBoxItem">
<Grid>
<WrapPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=tbText}">
<CheckBox IsChecked="{Binding Path=Completed}" Margin="5" />
<Button Style="{StaticResource ResourceKey=DeleteButtonTemplate}"
Margin="5" Click="btnDeleteItem_Click" />
<TextBox Name="tbText"
Text="{Binding Path=Text}"
Width="200"
TextWrapping="Wrap"
AcceptsReturn="True"
Margin="5"
Focusable="True"/>
<DatePicker Text="{Binding Path=Date}" Margin="5"/>
</WrapPanel>
</Grid>
</DataTemplate>

Resources