How to find visible elements inside of a ScrollViewer and WrapPanel - wpf

I've a WrapPanel surrounded with a ScrollViewer, I want to find the visible elements on the screen when I click a button.
My code like:
<ScrollViewer>
<WrapPanel>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
....
....
....
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
</WrapPanel>
</ScrollPanel>
How can I find the visible Label elements when ScrollViewer scrolled to some offset.

Hopefully this will get you started (you may want to change 'svViewportBounds.IntersectsWith' with 'Contains'.
public partial class MainWindow : Window
{
public string VisibleItems
{
get { return (string)GetValue(VisibleItemsProperty); }
set { SetValue(VisibleItemsProperty, value); }
}
public static readonly DependencyProperty VisibleItemsProperty =
DependencyProperty.Register("VisibleItems", typeof(string), typeof(MainWindow), new PropertyMetadata("??"));
public List<string> Items { get; private set; }
public MainWindow()
{
Items = new List<string>();
for (int i = 0; i < 25; ++i )
{
Items.Add("item_" + i);
}
DataContext = this;
InitializeComponent();
}
void OnScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = (ScrollViewer)sender;
var visibleItems = new List<int>();
Rect svViewportBounds = new Rect(sv.HorizontalOffset, sv.VerticalOffset, sv.ViewportWidth, sv.ViewportHeight);
for(int i = 0; i < Items.Count; ++i)
{
var container = itemsHost.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
if(container != null)
{
var offset = VisualTreeHelper.GetOffset(container);
var bounds = new Rect(offset.X, offset.Y, container.ActualWidth, container.ActualHeight);
if (svViewportBounds.IntersectsWith(bounds))
{
visibleItems.Add(i);
}
}
}
VisibleItems = string.Join(", ", visibleItems.ToArray());
}
}
<Window x:Class="WpfApplication59.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
WindowStartupLocation="CenterScreen"
Width="400"
Height="400">
<DockPanel>
<Border BorderThickness="1"
BorderBrush="Black"
DockPanel.Dock="Top">
<TextBlock Text="{Binding VisibleItems}"
Margin="5" />
</Border>
<ItemsControl ItemsSource="{Binding Items}" Name="itemsHost">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
ScrollChanged="OnScrollViewerScrollChanged">
<WrapPanel IsItemsHost="True" />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1"
BorderBrush="Black"
Width="100"
Height="100">
<Label Content="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Margin"
Value="4" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DockPanel>

Related

ListView Popup key functionality

I have implemented a listview that appears as a popup list. Now I would like to add key functionalities to it, like if whenever up arrow is pressed in a text box it should select an item in my list view and if pressing of KEY_UP/DOWN is continued it should continue changing its index respectively.
This is the EditMessageTextBox and associated EditMessageTagPopup
This is the XAML code used:
<Grid x:Name="EditGrid"
Grid.Row="1"
Visibility="{Binding EditMessageControlVisibility}"
FocusManager.IsFocusScope="False"
VerticalAlignment="Center"
Grid.Column="1"
HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border x:Name="EditMessageBorder"
Grid.Row="0"
BorderThickness="1"
CornerRadius="1"
Margin="0,10,0,0"
BorderBrush="Gray">
<Grid>
<TextBlock FontSize="16"
Margin="10,0,0,3"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Text="Edit message"
Foreground="{StaticResource brushWatermarkForeground}"
Visibility="{Binding ElementName=EditMessageTextBox, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="EditMessageTextBox"
Text="{Binding MessageToEdit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
BorderBrush="Transparent"
BorderThickness="0"
Foreground="Black"
FontSize="16"
Margin="8,1,1,1"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Left"
MinHeight="35"
ScrollViewer.VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap"
AcceptsReturn="False"
KeyUp="OnEditMessage_KeyUp"
SpellCheck.IsEnabled="true" />
</Grid>
</Border>
<StackPanel Grid.Row="1"
Margin="0,10"
Orientation="Horizontal">
<Button Background="Transparent"
VerticalContentAlignment="Center"
Padding="5,2,5,3"
Foreground="Black"
BorderBrush="Gray"
BorderThickness="0.8"
Width="100"
materialDesign:ShadowAssist.ShadowDepth="Depth0"
Click="EditMessageCancelButton_Clicked">Cancel</Button>
<Button Name="EditMessageButton"
VerticalContentAlignment="Center"
Padding="5,2,5,3"
Background="#007a5a"
Foreground="White"
BorderBrush="#007a5a"
Margin="15,0,0,0"
materialDesign:ShadowAssist.ShadowDepth="Depth0"
BorderThickness="0.8"
IsEnabled="True"
Width="140"
Content="Save Changes"
Click="EditMessageSaveButton_Clicked" />
</StackPanel>
<Popup x:Name="EditMessageTagPopup"
AllowsTransparency="True"
IsOpen="{Binding IsOpenTagPopUp}"
StaysOpen="False"
Placement="Top"
PlacementTarget="{Binding ElementName=EditMessageTextBox}">
<Border materialDesign:ShadowAssist.ShadowDepth="Depth5"
CornerRadius="5"
Background="White"
BorderBrush="Black"
BorderThickness="0.8"
MaxHeight="200">
<ListView x:Name="EditTaggedUsers"
Focusable="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource UserListForTag}}"
SelectionChanged="EditMessageTagList_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border Name="_Border"
Padding="8">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="_Border"
Property="Background"
Value="#FF3BD38E" />
<Setter Property="Foreground"
Value="White" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter TargetName="_Border"
Property="Background"
Value="#FF205B4B" />
<Setter Property="Foreground"
Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="-15,0,0,0"
Width="500">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
RadiusY="5"
RadiusX="5"
Height="20"
Width="20">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding ProfileImage}"
Stretch="UniformToFill" />
</Rectangle.Fill>
</Rectangle>
<TextBlock Grid.Column="1"
Text="{Binding FullName}"
Margin="-10,0,0,0" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</Grid>
and here is code behind:
ApplicationContext.StoredEditingMessage = (String)ApplicationContext.EditMessageText;
var messageData = ((TextBox)sender).DataContext as ChatsModel;
var EditMessagePopup = FindEditMessagePopup(MessageList);
Border EditEessageBorder = EditMessagePopup.Child as Border;
ListView EditMessageTagList = EditEessageBorder.Child as ListView;
Dispatcher?.Invoke(() =>
{
if (_contactsViewModel.GroupedChatByDate
.Find(x => messageData != null && x.MessageGuid == messageData.MessageGuid)
.IsOpenTagPopUp == false) return;
var index = _contactsViewModel.UsersListForTag.IndexOf(_contactsViewModel.UsersListForTag.FirstOrDefault(x => x.Selected == true));
switch (e.Key)
{
case Key.Up:
if (EditMessageTagList.SelectedIndex > 0)
{
EditMessageTagList.SelectedIndex -= 1;
EditMessageTagList.ScrollIntoView(EditMessageTagList.Items[EditMessageTagList.SelectedIndex]);
}
else
{
EditMessageTagList.SelectedIndex = _contactsViewModel.UsersListForTag.Count - 1;
EditMessageTagList.ScrollIntoView(EditMessageTagList.Items[EditMessageTagList.SelectedIndex]);
}
break;
case Key.Down:
if (EditMessageTagList.SelectedIndex + 1 == _contactsViewModel.UsersListForTag.Count)
{
EditMessageTagList.SelectedIndex = 0;
_contactsViewModel.UsersListForTag[index].Selected = true;
EditMessageTagList.ScrollIntoView(EditMessageTagList.Items[EditMessageTagList.SelectedIndex]);
}
else
{
EditMessageTagList.SelectedIndex += 1;
_contactsViewModel.UsersListForTag[index].Selected = true;
EditMessageTagList.ScrollIntoView(EditMessageTagList.Items[EditMessageTagList.SelectedIndex]);
}
break;
}
_contactsViewModel.UsersListForTag.ForEach(x => x.Selected = false);
if (index != -1)
{
_contactsViewModel.UsersListForTag[index].Selected = true;
}
});
I have tried adding an item in scroll into view() instead of selected index yet no update
when there is a perfect selection made this function is invoked from code behind
private void EditMessageTagList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
var messageModel = ((ListView)sender).DataContext as ChatsModel;
if (((ListView)sender).SelectedItem is UserModel selectedUserForTag)
{
// _contactsViewModel.GroupedChatByDate.Find(x => messageModel != null && x.MessageGuid == messageModel.MessageGuid) .IsOpenTagPopUp = false;
string SelectedTag = (selectedUserForTag.Id == ApplicationContext.CurrentLoggedInUserGuid) ? $"{selectedUserForTag.UserName.Replace("(you) ", "")} " : $"{selectedUserForTag.UserName} ";
_contactsViewModel.GroupedChatByDate.Find
(x => messageModel != null && x.MessageGuid == messageModel.MessageGuid)
.MessageToEdit = "#" + SelectedTag;
}
// ((ListView) sender).SelectedItem = null;
}
catch (Exception exception)
{
LoggingManager.Error(exception);
}
}
Here is screen recording regarding issue
and
Here is working functionality
The problem is that after each navigation to the next item of the ListView you want to set focus to the selection TextBox which binds to the SelectedItem. Otherwise navigating the items of a ListView with the help of the arrow keys is already the default behavior of the ListView.
The simplest solution is to capture the keyboard input using UIElement.InputBinding on the selection TextBox (which enables to handle the keys in the view model) and then
Select the next/previous item
Scroll the SelectedItem into view
Move the focus to the selection TextBox
Move the caret of the selection TextBox to the end
DataItem.cs
class DataItem
{
public string FullName { get; set; }
public DataItem(string fullName) => this FullName = fullName;
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<DataItem> DataItems { get; set; }
public ICommand SelectNextCommand => new AsyncRelayCommand(SelectNextItem);
public ICommand SelectPreviousCommand => new AsyncRelayCommand(SelectPreviousItem);
private bool IsSelectedItemChangeInternal { get; set; }
private DataItem selectedDataItem;
public DataItem SelectedDataItem
{
get => this.selectedDataItem;
set
{
this.selectedDataItem = value;
OnPropertyChanged();
// Do not filter the list when the selected item was set by the user
// e.g. by using arrow keys
if (!this.IsSelectedItemChangeInternal)
{
UpdateSearchFilter();
}
}
}
private string filterKey;
public string FilterKey
{
get => this.filterKey;
set
{
this.filterKey = value;
OnPropertyChanged();
// Only apply filters when the FilterKey was changed by the user
// e.g. by editing the edit TextBox that binds to this property
if (!this.IsSelectedItemChangeInternal)
{
ApplySearchFilter();
}
}
}
public ViewModel()
{
this.DataItems = new ObservableCollection<DataItems>();
for (var index = 0; index < 100; index++)
{
this.DataItems.Add(new DataItem("name " + index.ToString());
}
}
private void ApplySearchFilter()
{
ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Games);
this.IsSelectedItemChangeInternal = true;
collectionView.Filter = item =>
string.IsNullOrWhiteSpace(this.FilterKey) || (item as DetailItem).FullName.StartsWith(this.FilterKey);
// pre-select the first match
collectionView.MoveCurrentToFirst();
this.IsSelectedItemChangeInternal = false;
}
private void UpdateSearchFilter()
{
this.IsSelectedItemChangeInternal = true;
this.FilterKey = this.SelectedDataItem.FullName;
this.IsSelectedItemChangeInternal = false;
}
private void SelectNextItem()
{
ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.DataItems);
collectionView.MoveCurrentToNext();
// Loop
if (collectionView.IsCurrentAfterLast)
{
collectionView.MoveCurrentToFirst();
}
}
private void SelectPreviousItem()
{
ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.DataItems);
collectionView.MoveCurrentToPrevious();
// Loop
if (collectionView.IsCurrentBeforeFirst)
{
collectionView.MoveCurrentToLast();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName);
}
}
MainWindow.xaml.cs
private void AdjustFocus_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = sender as ListView;
listView.ScrollIntoView(listView.SelectedItem);
Application.Current.Dispatcher.InvokeAsync(() =>
{
Keyboard.Focus(this.EditMessageTextBox);
this.EditMessageTextBox.CaretIndex = this.EditMessageTextBox.Text.Length;
});
}
private void AdjustFocus_OnOpened(object sender, EventArgs e)
{
this.EditTaggedUsers.Focus();
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContex>
<Grid>
<TextBox x:Name="EditMessageTextBox"
Text="{Binding FilterKey}">
<TextBox.InputBindings>
<KeyBinding Key="Down"
Command="{Binding SelectNextCommand}" />
<KeyBinding Key="Up"
Command="{Binding SelectPreviousCommand}" />
</TextBox.InputBindings>
</TextBox>
<Popup IsOpen="True"
Opened="AdjustFocus_OnOpened"
StaysOpen="False"
Placement="Top"
PlacementTarget="{Binding ElementName=EditMessageTextBox}">
<ListView IsSynchronizedWithCurrentItem="True"
Height="400"
SelectedItem="{Binding SelectedDataItem}"
ItemsSource="{Binding DataItems}"
SelectionChanged="AdjustFocus_OnSelectionChanged">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type DataItem}">
<TextBox Text="{Binding FullName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Popup>
</Grid>
</Window>
Remarks
As the navigation is done using the CollectionView of the ItemsSource the ListView.IsSynchronizedWithCurrentItem property must be set to true. Otherwise the navigation of the CollectionView won't effect the view.

Custom User Control not binding data [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Datacontext conflicts
(1 answer)
How to correctly bind to a dependency property of a usercontrol in a MVVM framework
(4 answers)
Closed 5 years ago.
I've created a user control that have a label and textbox.
i added two DependencyProperties (Text and Label) and bind them to textbox.text and label.content.
however, i'm not able to see the text of textbox.
in the main window, when i'm not binding to any element the label is shown but if i binding the element is not shown. the textbox not showing either way.
here's the xaml:
<UserControl x:Class="TestNewLabeltextbox.UserControl1"
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"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Horizontal" Background="White" FlowDirection="RightToLeft">
<Label x:Name="lbl" Content="{Binding Label, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" HorizontalAlignment="Left" Background="blue">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<StackPanel Orientation="Horizontal">
<Border Background="Blue" Width="200" BorderThickness="0,0,0,0">
<StackPanel Orientation="Horizontal">
<Viewbox StretchDirection="DownOnly" Stretch="Uniform">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True" TextBlock.FontSize="14" TextBlock.Foreground="#FFFFFF" Margin="5">
<ContentPresenter.Effect>
<DropShadowEffect BlurRadius="0.0"
Color="#032A6B"
Direction="90"
Opacity="1"
ShadowDepth="1" />
</ContentPresenter.Effect>
</ContentPresenter>
</Viewbox>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Label.Style>
</Label>
<TextBox x:Name="txt" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="120" HorizontalAlignment="Right">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border CornerRadius="0,0,0,50" BorderBrush="Black" Background="White" BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True" TextBlock.FontSize="14" TextBlock.Foreground="#FFFFFF" Margin="5">
<ContentPresenter.Effect>
<DropShadowEffect BlurRadius="0.0"
Color="#032A6B"
Direction="90"
Opacity="1"
ShadowDepth="1" />
</ContentPresenter.Effect>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
Here'sUserControl1.cs:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.DataContext = this;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string Label
{
get { return (string)this.GetValue(LabelProperty); }
set { this.SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string Text
{
get { return (string)this.GetValue(TextProperty); }
set { this.SetValue(TextProperty, value); }
}
}
here's the window xaml + cs:
<Window x:Class="TestNewLabeltextbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TestNewLabeltextbox"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical" Height="150">
<controls:UserControl1 Text="hello" Height="50" Label="{Binding Hello, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<controls:UserControl1 Text="hello" Height="50" Label="world" />
<Label BorderBrush="Black" BorderThickness="2" Width="100" Height="50" Content="{Binding Hello, Mode=TwoWay}"/>
</StackPanel>
</Grid>
public partial class MainWindow : Window
{
ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
vm.Hello = "555";
this.DataContext = vm;
}
}
viewmodel.cs
public class ViewModel : INotifyPropertyChanged
{
private string h = "Hello";
public string Hello
{
get
{
return h;
}
set
{
h = value;
NotifyPropertyChanged("Hello");
}
}
#region "PropertyChanged Event"
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Default Source of binding is DataContext. But your Label and Text dependency properties defined in the control rather than in view-model. Change binding of Label to
{Binding Path=Label, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}
and binding of TextBox to
{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
Please read about Mode and UpdateSourceTrigger properties of Binding. It seems that you don't know how they work. Mode=TwoWay, UpdateSourceTrigger=PropertyChanged doesn't make any sense for Content property.

WPF Binding two levels of UserControl

let's explain a little bit my issue, i wrote a code of an application that show a usercontrol with bindings that i'm trying to set to another sub-usercontrol. I'm using a model that i've set to my MainWindow.xaml.
THE PROBLEM IS:
The usercontrol innerusercontrol doesn't show changes in the bindings. I guess i made a mistake, but i don't know where and how to fix it. I read several posts but no body answer them with a specific code-line. Could any body help me?
I'll appreciate an answer. Thanks
MainWindow.xaml
<Window x:Class="DependencyProperties.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DependencyProperties"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="3*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:DisplayProduct x:Name="ucDisplay" Grid.Row="1" Height="110"/>
<Button Grid.Row="2" Click="Button_Click">Apply circle visibility false/true</Button>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static ProductModel product;
public MainWindow()
{
InitializeComponent();
product = new ProductModel() { IsActivo = true, Descuento = 15.00, Importe = 100.00, Total = 85.00 };
ucDisplay.DataContext = product;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Antes " + product.IsActivo);
product.IsActivo = !product.IsActivo;
Console.WriteLine("Despues" + product.IsActivo);
}
}
DisplayProductUserControl.xaml
<UserControl x:Class="DependencyProperties.DisplayProductUserControl"
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"
xmlns:local="clr-namespace:DependencyProperties"
mc:Ignorable="d" x:Name="win_Display"
d:DesignHeight="80" d:DesignWidth="500">
<UserControl.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontWeight" Value="Light"/>
</Style>
</UserControl.Resources>
<Grid>
<Viewbox>
<Grid Width="{Binding ElementName=win_Display, Path=ActualWidth}" Height="{Binding ElementName=win_Display, Path=ActualHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Price: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Price}"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="Discount: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Discount}"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<TextBlock Text="Total: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Total}"/>
</StackPanel>
<local:InnerUserControl Grid.Column="0" Grid.Row="1">
<local:InnerUserControl DataContext="{Binding Path=IsActive,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"/>
</local:InnerUserControl>
</Grid>
</Viewbox>
</Grid>
</UserControl>
InnerUserControl *THE PROBLEM IS HERE <------------------ *
<UserControl x:Class="DependencyProperties.InnerUserControl"
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" x:Name="win_inner"
d:DesignHeight="40" d:DesignWidth="100">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter"/>
</UserControl.Resources>
<Grid Width="{Binding ElementName=win_inner, Path=ActualWidth}">
<TextBlock Text="{Binding Path=IsActive}" FontSize="15"/>
**<!-- HERE IS THE PROBLEM I DOESN'T SHOW CHANGES WHEN IsActive is false/true -->**
<Ellipse Height="{Binding ElementName=win_inner, Path=ActualHeight}" Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}" Fill="Red"
Visibility="{Binding Path=IsActive, Converter={StaticResource VisibilityConverter}}" HorizontalAlignment="Right"></Ellipse>
</Grid>
</UserControl>
ProductoModel
public class ProductModel : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set { price= value; }
}
private double discount;
public double Discount
{
get { return discount; }
set { discount= value; }
}
private double total;
public double Total
{
get { return total; }
set { total = value; }
}
private bool isActive;
public bool IsActive
{
get { return isActive; }
set { isActive = value; }
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
like dnr3 commented already,
Add RaisePropertyChanged method in your 'Product Model' class.
public bool IsActive
{
get { return isActive; }
set
{
isActive = value;
RaisePropertyChanged("IsActive");
}
}
View never changes until it gets 'PropertyChanged' Event.

How to use ColumnDefinitions.Add correctly?

I copied an example about dock panel from "WPF 4.5 Unleashed" ch.5. In the example the dock is set to be in the right of the grid So when in the code behind layer0.ColumnDefinitions.Add(column1CloneForLayer0); called, a column with content column1CloneForLayer0 will be created in the right side of the grid.
In my project I almost did the same thing, but when I call the function to add a column with some content, some empty space's created at the right side.. which is not as expected.So what's the right thing to do?
1.How to Add a column in the left side of a grid?
2.In the example the column was created yet why it's empty? Maybe Do i need to set the Z Index?
Update
I added something similar in the original example in the book and get wrong result. Below is the the source codes. I just added a leftToolBox in the left.
https://www.dropbox.com/sh/61hm139j77kz9k1/AACKqhG5uXFkQgnt8fWi4NvNa?dl=0
Update2
Relevant codes: In this case I just add a stackpanel with button in the left side and and click to call DocakPane(3), instead of giving the new column in the left, it creates column in the right.
public void DockPane(int paneNumber)
{
if (paneNumber == 1)
{
pane1Button.Visibility = Visibility.Collapsed;
pane1PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
// Add the cloned column to layer 0:
layer0.ColumnDefinitions.Add(column1CloneForLayer0);
// Add the cloned column to layer 1, but only if pane 2 is docked:
if (pane2Button.Visibility == Visibility.Collapsed) layer1.ColumnDefinitions.Add(column2CloneForLayer1);
}
else if (paneNumber == 2)
{
pane2Button.Visibility = Visibility.Collapsed;
pane2PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
// Add the cloned column to layer 0:
layer0.ColumnDefinitions.Add(column2CloneForLayer0);
// Add the cloned column to layer 1, but only if pane 1 is docked:
if (pane1Button.Visibility == Visibility.Collapsed) layer1.ColumnDefinitions.Add(column2CloneForLayer1);
}
else
{
leftpane1Button.Visibility = Visibility.Collapsed;
pane3PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
layer0.ColumnDefinitions.Add(testcol);
}
}
Based on the sample project provided, here is a re-write of the same using MVVM and most of the problem related to hard-coding would are gone. It is not pure MVVM but it is mostly MVVM to get started with.
start by defining ViewModels
a class to represent the dock pane
class DockablePaneVM : ViewModelBase
{
public DockablePaneVM(DockHostVM host)
{
Host = host;
}
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
private bool _isPinned;
public bool IsPinned
{
get { return _isPinned; }
set
{
_isPinned = value;
Host.PinModeChanged(this);
RaisePropertyChanged("IsPinned");
}
}
private object _content;
public object Content
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged("Content");
}
}
public DockHostVM Host { get; private set; }
public Dock Dock { get; set; }
}
host for the dockpanes, I have used collectionview for simplifying the location
class DockHostVM : ViewModelBase
{
public DockHostVM()
{
Panes = new ObservableCollection<DockablePaneVM>();
LeftPanes = new CollectionViewSource() { Source = Panes }.View;
RightPanes = new CollectionViewSource() { Source = Panes }.View;
FlotingLeftPanes = new CollectionViewSource() { Source = Panes }.View;
FlotingRightPanes = new CollectionViewSource() { Source = Panes }.View;
LeftPanes.Filter = o => Filter(o, Dock.Left, true);
RightPanes.Filter = o => Filter(o, Dock.Right, true);
FlotingLeftPanes.Filter = o => Filter(o, Dock.Left, false);
FlotingRightPanes.Filter = o => Filter(o, Dock.Right, false);
}
private bool Filter(object obj, Dock dock, bool isPinned)
{
DockablePaneVM vm = obj as DockablePaneVM;
return vm.Dock == dock && vm.IsPinned == isPinned;
}
public ObservableCollection<DockablePaneVM> Panes { get; set; }
public ICollectionView LeftPanes { get; set; }
public ICollectionView RightPanes { get; set; }
public ICollectionView FlotingLeftPanes { get; set; }
public ICollectionView FlotingRightPanes { get; set; }
private object _content;
public object Content
{
get { return _content; }
set { _content = value; }
}
public void PinModeChanged(DockablePaneVM paneVM)
{
LeftPanes.Refresh();
RightPanes.Refresh();
FlotingLeftPanes.Refresh();
FlotingRightPanes.Refresh();
}
//sample generator
public static DockHostVM GetSample()
{
DockHostVM vm = new DockHostVM();
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Left Toolbox", Content = new ToolBoxVM() });
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Solution Explorer", Content = new SolutionExplorerVM(), Dock = Dock.Right });
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Toolbox", Content = new ToolBoxVM(), Dock = Dock.Right });
return vm;
}
}
then styles, to give provide the view for the classes
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:VisualStudioLikePanes">
<DataTemplate DataType="{x:Type l:DockablePaneVM}">
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Padding="8,4"
Text="{Binding Title}"
TextTrimming="CharacterEllipsis"
Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" />
<ToggleButton IsChecked="{Binding IsPinned}"
Grid.Column="1">
<Image Name="pinImage"
Source="pinHorizontal.gif" />
</ToggleButton>
<ContentControl Content="{Binding Content}"
Grid.Row="1"
Grid.ColumnSpan="2" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsPinned}"
Value="True">
<Setter Property="Source"
TargetName="pinImage"
Value="pin.gif" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<Style TargetType="TabItem">
<Setter Property="Header"
Value="{Binding Title}" />
</Style>
<Style TargetType="TabItem"
x:Key="FloterItem"
BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="90" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabControl">
<Setter Property="l:TabControlHelper.IsLastItemSelected"
Value="True" />
<Style.Triggers>
<Trigger Property="HasItems"
Value="false">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="TabControl"
x:Key="AutoResizePane"
BasedOn="{StaticResource {x:Type TabControl}}">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="false">
<Setter Property="Width"
Value="23" />
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type l:ToolBoxVM}">
<ListBox Padding="10"
Grid.Row="1">
<ListBoxItem>Button</ListBoxItem>
<ListBoxItem>CheckBox</ListBoxItem>
<ListBoxItem>ComboBox</ListBoxItem>
<ListBoxItem>Label</ListBoxItem>
<ListBoxItem>ListBox</ListBoxItem>
</ListBox>
</DataTemplate>
<DataTemplate DataType="{x:Type l:SolutionExplorerVM}">
<TreeView Grid.Row="2">
<TreeViewItem Header="My Solution" IsExpanded="True">
<TreeViewItem Header="Project #1" />
<TreeViewItem Header="Project #2" />
<TreeViewItem Header="Project #3" />
</TreeViewItem>
</TreeView>
</DataTemplate>
</ResourceDictionary>
I have also created some dummy classes to represent the toolbox and solution explorer
also a helper class to improve the usability of tab control which i have used to host the dockpanes
class TabControlHelper
{
public static bool GetIsLastItemSelected(DependencyObject obj)
{
return (bool)obj.GetValue(IsLastItemSelectedProperty);
}
public static void SetIsLastItemSelected(DependencyObject obj, bool value)
{
obj.SetValue(IsLastItemSelectedProperty, value);
}
// Using a DependencyProperty as the backing store for IsLastItemSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLastItemSelectedProperty =
DependencyProperty.RegisterAttached("IsLastItemSelected", typeof(bool), typeof(TabControlHelper), new PropertyMetadata(false, OnIsLastItemSelected));
private static void OnIsLastItemSelected(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TabControl tc = d as TabControl;
tc.Items.CurrentChanged += (ss, ee) =>
{
if (tc.SelectedIndex < 0 && tc.Items.Count > 0)
tc.SelectedIndex = 0;
};
}
}
this will keep an item selected any time, in this project it will be used when a dock pane is pinned/unpinned
now the main window, note that I have bounded the dock Panes to 4 tab controls, left, right, left float & right float
<Window x:Class="VisualStudioLikePanes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="500"
Width="800"
WindowStartupLocation="CenterScreen"
xmlns:l="clr-namespace:VisualStudioLikePanes">
<Window.DataContext>
<ObjectDataProvider MethodName="GetSample"
ObjectType="{x:Type l:DockHostVM}" />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="FILE" />
...
<MenuItem Header="HELP" />
</Menu>
<DockPanel LastChildFill="True"
Grid.Row="1">
<Border Width="23"
DockPanel.Dock="Left"
Visibility="{Binding Visibility, ElementName=LeftFloter}" />
<Border Width="23"
DockPanel.Dock="Right"
Visibility="{Binding Visibility, ElementName=RightFloter}" />
<TabControl ItemsSource="{Binding LeftPanes}"
DockPanel.Dock="Left" />
<TabControl ItemsSource="{Binding RightPanes}"
DockPanel.Dock="Right" />
<Grid Name="layer0">
... page content
</Grid>
</DockPanel>
<TabControl ItemsSource="{Binding FlotingLeftPanes}"
Grid.Row="1"
HorizontalAlignment="Left"
TabStripPlacement="Left"
Style="{StaticResource AutoResizePane}"
ItemContainerStyle="{StaticResource FloterItem}"
x:Name="LeftFloter" />
<TabControl ItemsSource="{Binding FlotingRightPanes}"
Grid.Row="1"
HorizontalAlignment="Right"
TabStripPlacement="Right"
Style="{StaticResource AutoResizePane}"
ItemContainerStyle="{StaticResource FloterItem}"
x:Name="RightFloter" />
</Grid>
</Window>
result is your expected behavior with MVVM approach, adding new panel is easy as Panes.Add(new DockablePaneVM(vm) { Title = "Left Toolbox", Content = new ToolBoxVM() }); rest is handled.
Demo
download the working sample VisualStudioLikePanes.zip

Content control binding not working

When i set the content of the content control like below but the binding of the element inside the content get break.
i have given a content inside a property of a class and set the property as the content to the content control.
[Xmal]
<Grid>
<Button HorizontalAlignment="Center"
VerticalAlignment="Top"
Click="Button_Click_1"
Content="Click" />
<local:MyTile x:Name="mytile">
<local:MyTile.TileViewContent>
<StackPanel>
<TextBox x:Name="text"
Background="Red"
Text="MyText" />
<TextBox Text="{Binding ElementName=text, Path=Text,Mode=TwoWay}" />
</StackPanel>
</local:MyTile.TileViewContent>
</local:MyTile>
<ContentControl x:Name="contentcontrol" />
</Grid>
[C#]
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
contentcontrol.Content = mytile.TileViewContent;
}
}
public class MyTile:Control
{
public FrameworkElement TileViewContent
{
get { return (FrameworkElement)GetValue(TileViewContentProperty); }
set { SetValue(TileViewContentProperty, value); }
}
public static readonly DependencyProperty TileViewContentProperty =
DependencyProperty.RegisterAttached("TileViewContent", typeof(FrameworkElement), typeof(MyTile), new PropertyMetadata(null));
}
When i set the content the binding not working. please help
If you want to simply binding is works, not necessarily through ContentControl, use the Style for your element:
<Window.Resources>
<Style x:Key="MyTemplateForMyControl" TargetType="{x:Type local:MyTile}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyTile}">
<StackPanel>
<TextBox x:Name="MyTextBox" Text="MyText" Background="Red" />
<TextBox Text="{Binding ElementName=MyTextBox, Path=Text}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Click" Click="Button_Click_1" />
<local:MyTile x:Name="MyTile" />
</Grid>
In code we are set the Style for your Control:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MyTile.Style = this.Resources["MyTemplateForMyControl"] as Style;
}
If it is necessary to use ContentControl I can recommend instead of you Control use a DataTemplate:
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyDataForTemplate}">
<StackPanel>
<TextBox x:Name="MyTextBox" Text="{Binding TextBoxContent}" Background="Red" />
<TextBox Text="{Binding ElementName=MyTextBox, Path=Text}" />
</StackPanel>
</DataTemplate>
<!-- Some data -->
<local:MyDataForTemplate x:Key="MyDataForTile" TextBoxContent="MyText" />
</Window.Resources>
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Click" Click="Button_Click_1" />
<ContentControl Name="TileContentControl" />
</Grid>
There will be some data for a template:
public class MyDataForTemplate
{
string textBoxContent = "";
/// <summary>
/// Text for TextBox
/// </summary>
public string TextBoxContent
{
get
{
return textBoxContent;
}
set
{
textBoxContent = value;
}
}
}
In code we are set the data for a template:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
TileContentControl.Content = this.Resources["MyDataForTile"] as MyDataForTemplate;
}

Resources