I am trying to create a user move-able expander for my WPF project. Ideally, if the user clicks the expander button, s/he should be able to expand. If one clicks and drags the text box that say "Advanced Options" I want that person to be able to move the expander around. My code doesn't work and just expands the expander. I tried associating the click on the header with something different than expanding, but that doesn't seem to work. The xaml and C# code is below. Help?
<Expander HorizontalAlignment="Left" Margin="89,372,0,0" VerticalAlignment="Top" Height="453" Width="909" IsExpanded="True" x:Name="grid_expander">
<Expander.Header>
<TextBlock HorizontalAlignment="Left" Height="22" Margin="5,0,0,0" TextWrapping="Wrap" Width="154" Foreground="White" FontSize="18" MouseDown="grid_expander_MouseDown" MouseUp="grid_expander_MouseUp" MouseMove="grid_expander_MouseMove">
Advanced Options
</TextBlock>
</Expander.Header>
<Expander.RenderTransform>
<TranslateTransform x:Name="expander_xy"/>
</Expander.RenderTransform>
</Expander>
private void grid_expander_MouseDown(object sender, MouseButtonEventArgs e)
{
//e.Handled = true;
m_start = e.GetPosition(window);
m_startOffset = new Vector(expander_xy.X, expander_xy.Y);
grid_expander.CaptureMouse();
}
private void grid_expander_MouseMove(object sender, MouseEventArgs e)
{
if (grid_expander.IsMouseCaptured)
{
Vector offset = Point.Subtract(e.GetPosition(window), m_start);
expander_xy.X = m_startOffset.X + offset.X;
expander_xy.Y = m_startOffset.Y + offset.Y;
}
}
private void grid_expander_MouseUp(object sender, MouseButtonEventArgs e)
{
grid_expander.ReleaseMouseCapture();
}
I have did the same using an Image Control instead of Expander Control, I believe you can replace the Image Control and place your Expander Control I will share my code. Try doing with Expander Control Let me know if it helps.
XAML
<Grid
Background="Black"
mousebehav:MouseBehaviour.MouseLeftButtonUpCommand="{Binding MLBUCommand}">
<Canvas >
<Image RenderTransformOrigin="0.5,0.5"
Source="{Binding ImageSource}"
Stretch="None" SnapsToDevicePixels="False"
mousebehav:MouseBehaviour.MouseLeftButtonDownCommand="{Binding MDCommand}"
mousebehav:MouseBehaviour.MouseMoveCommand="{Binding MMCommand}">
<Image.RenderTransform>
<TranslateTransform
X="{Binding MouseX}"
Y="{Binding MouseY}" />
</Image.RenderTransform>
<Image.LayoutTransform>
<TransformGroup>
<ScaleTransform
ScaleX="{Binding ScaleX}"
ScaleY="{Binding ScaleY}"/>
<RotateTransform
Angle="{Binding RotateAngle}"/>
</TransformGroup>
</Image.LayoutTransform>
</Image>
</Canvas>
</Grid>
View Model
private double _ScaleX;
public double ScaleX
{
get { return _ScaleX; }
set { _ScaleX = value; NotifyPropertyChanged(); }
}
private double _ScaleY;
public double ScaleY
{
get { return _ScaleY; }
set { _ScaleY = value; NotifyPropertyChanged(); }
}
private double _RotateAngle;
public double RotateAngle {
get { return _RotateAngle; }
set { _RotateAngle = value; NotifyPropertyChanged(); }
}
private double _MouseX;
public double MouseX
{
get { return _MouseX; }
set { _MouseX = value; NotifyPropertyChanged(); }
}
private double _MouseY;
public double MouseY
{
get { return _MouseY; }
set { _MouseY = value; NotifyPropertyChanged(); }
}
public bool IsMouseCaptured { get; set; }
private RelayCommand _mouseDownCommand;
public RelayCommand MDCommand
{
get
{
if (_mouseDownCommand == null) return _mouseDownCommand = new RelayCommand(param => ExecuteMouseDown((MouseEventArgs)param));
return _mouseDownCommand;
}
set { _mouseDownCommand = value; }
}
System.Windows.Point start;
System.Windows.Point origin;
private void ExecuteMouseDown(MouseEventArgs e)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
start = e.GetPosition(border);
origin = new System.Windows.Point(MouseX, MouseY);
IsMouseCaptured = true;
}
private RelayCommand _mouseMoveCommand;
public RelayCommand MMCommand
{
get
{
if (_mouseMoveCommand == null) return _mouseMoveCommand = new RelayCommand(param => ExecuteMouseMove((MouseEventArgs)param));
return _mouseMoveCommand;
}
set { _mouseMoveCommand = value; }
}
private void ExecuteMouseMove(MouseEventArgs e)
{
if(IsMouseCaptured)
{
var image = (System.Windows.Controls.Image)e.OriginalSource;
var border = (IInputElement)image.Parent;
Vector v = start - e.GetPosition(border);
MouseX = origin.X - v.X;
MouseY = origin.Y - v.Y;
}
}
private RelayCommand _MLBUCommand;
public RelayCommand MLBUCommand
{
get
{
if (_MLBUCommand == null) return _MLBUCommand = new RelayCommand(param => Execute_MLBU((MouseEventArgs)param));
return _MLBUCommand;
}
set { _MLBUCommand = value; }
}
private void Execute_MLBU(MouseEventArgs param)
{
IsMouseCaptured = false;
}
Mouse Behaviors as Attached properties
public class MouseBehaviour
{
#region MouseUp
public static readonly DependencyProperty MouseUpCommandProperty =
DependencyProperty.RegisterAttached("MouseUpCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseUpCommandChanged)));
private static void MouseUpCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseUp += element_MouseUp;
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseUpCommand(element);
command.Execute(e);
}
public static void SetMouseUpCommand(UIElement element, ICommand value)
{
element.SetValue(MouseUpCommandProperty, value);
}
public static ICommand GetMouseUpCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseUpCommandProperty);
}
#endregion
#region MouseDown
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseDownCommandChanged)));
private static void MouseDownCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseDown += element_MouseDown;
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseDownCommand(element);
command.Execute(e);
}
public static void SetMouseDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseDownCommandProperty, value);
}
public static ICommand GetMouseDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseDownCommandProperty);
}
#endregion
#region MouseMove
public static readonly DependencyProperty MouseMoveCommandProperty =
DependencyProperty.RegisterAttached("MouseMoveCommand", typeof(ICommand), typeof(MouseBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(MouseMoveCommandChanged)));
private static void MouseMoveCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
element.MouseMove += new MouseEventHandler(element_MouseMove);
}
static void element_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
ICommand command = GetMouseMoveCommand(element);
command.Execute(e);
}
public static void SetMouseMoveCommand(UIElement element, ICommand value)
{
element.SetValue(MouseMoveCommandProperty, value);
}
public static ICommand GetMouseMoveCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseMoveCommandProperty);
}
#endregion
}
Related
I would like to show/activate a hidden window and set the (keyboard-)focus to a textbox programmatically (without violating mvvm if possible).
So if the the user presses a hotkey (already implemented) following should happen:
Window gets shown + activated
Textbox gets focused + keyboard focus (so the user can type some text)
Pressing the hotkey again should hide the window.
I tried binding Activated and Visibility to a boolean value, but I ran into some problems with TwoWay-Binding. Thank you for any ideas on how to solve this problem.
Edit:
MainWindow.xml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
<i:Interaction.Behaviors>
<bh:ActivateBehavior Activated="{Binding Visible, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<Window.Visibility>
<Binding Path="Visible" Mode="TwoWay" Converter="{StaticResource BoolToVisConverter}"/>
</Window.Visibility>
<TextBox Grid.Column="1" x:Name="SearchBar" bh:FocusExtension.IsFocused="{Binding Visible, Mode=TwoWay}"/>
ActivateBehavior.cs:
used from this https://stackoverflow.com/a/12254217/13215602
public class ActivateBehavior : Behavior<Window> {
Boolean isActivated;
public static readonly DependencyProperty ActivatedProperty =
DependencyProperty.Register(
"Activated",
typeof(Boolean),
typeof(ActivateBehavior),
new PropertyMetadata(OnActivatedChanged)
);
public Boolean Activated {
get { return (Boolean) GetValue(ActivatedProperty); }
set { SetValue(ActivatedProperty, value); }
}
static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
var behavior = (ActivateBehavior) dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;
// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
if (behavior.AssociatedObject.WindowState == WindowState.Minimized)
behavior.AssociatedObject.WindowState = WindowState.Normal;
behavior.AssociatedObject.Activate();
}
protected override void OnAttached() {
AssociatedObject.Activated += OnActivated;
AssociatedObject.Deactivated += OnDeactivated;
}
protected override void OnDetaching() {
AssociatedObject.Activated -= OnActivated;
AssociatedObject.Deactivated -= OnDeactivated;
}
void OnActivated(Object sender, EventArgs eventArgs) {
this.isActivated = true;
Activated = true;
}
void OnDeactivated(Object sender, EventArgs eventArgs) {
this.isActivated = false;
Activated = false;
}
}
FocusExtension.cs: used from this https://stackoverflow.com/a/1356781/13215602
public static class FocusExtension
{
public static bool GetIsFocuses(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement) d;
if ((bool) e.NewValue)
{
Keyboard.Focus(uie);
uie.Focus();
}
}
}
MainViewModel.cs:
private bool mVisible;
//Binding to this variable
public bool Visible
{
get { return mVisible; }
set
{
if (mVisible == value)
return;
mVisible = value;
RaisePropertyChanged(nameof(Visible));
}
}
I have a ToggleSwitch UserControl. ToggleSwitch has DependencyProperty called IsChecked and this shows check status of UserControl
ToggleSwitch.xaml.cs :
public bool IsChecked {
get { return ( bool ) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleSwitch),
new PropertyMetadata(false, IsCheckedPropertyOnChanged));
private static void IsCheckedPropertyOnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if ( obj is ToggleSwitch ) {
var res = ( ( ToggleSwitch ) obj );
res.ChangeStatus();
}
}
At the same time IsChecked status can change when 'GridMouseLeftButtonDown' event occurs (So the user click the ToggleSwitch):
private void GridOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
IsChecked = !IsChecked;
}
The appearance changes with the ChangeStatus function.
Below code shows how I bind the IsChecked in MainWindow.xaml
<local:ToggleSwitch IsChecked="{Binding SelectedPerson.IsActive}"/>
SelectedPersonis a DependencyProperty in MainWindow.xaml.cs.
The IsChecked property changes successfully every time I create a new instance of SelectedPerson.
But once GridOnMouseLeftButtonDown (ToggleSwitch ClickEvent) fires, the IsCheckedPropertyOnChanged no longer invokes.
Small project example to show the problem:
ToggleSwitch.xaml.cs:
public partial class ToggleSwitch : UserControl {
public bool IsChecked {
get { return ( bool ) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleSwitch),
new PropertyMetadata(false, IsCheckedPropertyOnChanged));
private static void IsCheckedPropertyOnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
if ( obj is ToggleSwitch ) {
var res = ( ( ToggleSwitch ) obj );
res.ChangeStatus();
}
}
public ToggleSwitch() {
InitializeComponent();
}
private void ChangeStatus() {
if ( IsChecked ) {
MainContainer.Background = Brushes.Green;
} else {
MainContainer.Background = Brushes.Red;
}
}
private void GridOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
IsChecked = !IsChecked;
}
}
ToggleSwitch.xaml:
<Grid MouseLeftButtonDown="GridOnMouseLeftButtonDown" Width="150" Height="150">
<Border x:Name="MainContainer" Width="150" Height="150" BorderThickness="1" BorderBrush="Black" Background="White"/>
</Grid>
MainWindow.xaml.cs:
public class Person {
public bool IsActive { get; set; }
}
public partial class MainWindow : Window {
public Person SelectedPerson {
get { return ( Person ) GetValue(SelectedPersonProperty); }
set { SetValue(SelectedPersonProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedPerson. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedPersonProperty =
DependencyProperty.Register("SelectedPerson", typeof(Person), typeof(MainWindow));
public MainWindow() {
InitializeComponent();
this.DataContext = this;
SelectedPerson = new Person {
IsActive = true
};
}
private void ButtonOnClicked(object sender, RoutedEventArgs e) {
var change = !SelectedPerson.IsActive;
SelectedPerson = new Person {
IsActive = change
};
}
}
MainWindow.xaml:
<StackPanel Orientation="Vertical">
<local:ToggleSwitch IsChecked="{Binding SelectedPerson.IsActive}" />
<Button Content="ChangeItemSource" Click="ButtonOnClicked" Width="150" Margin="50" />
</StackPanel>
I need to animate a row in certain circumstances. I have a Behaivor that only runs when a certain property changes. The problem I have is that when the behaivor ends, I lose all the animations I had before. Watch the mouse over before and after the animation. Also, when finished, observe when I click on the row. Any solution?
.gif
public enum Events
{
Changed
}
public class AnimateBehavior : Behavior<TextBlock>
{
public Events Property { get; set; }
private ColorAnimation ColorAnimation { get; set; }
public Brush BackgroundBrush { get; set; }
protected override void OnAttached()
{
ColorAnimation = new ColorAnimation
{
AutoReverse = true,
To = Colors.LightBlue,
From = Colors.Transparent,
FillBehavior = FillBehavior.Stop,
RepeatBehavior = new RepeatBehavior(3),
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
AssociatedObject.DataContextChanged += OnDataContextChanged;
WireEvents();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is INotifyPropertyChanged oldData)
{
oldData.PropertyChanged -= OnPropertyChanged;
}
WireEvents();
}
private void WireEvents()
{
if (AssociatedObject.DataContext is INotifyPropertyChanged currentData)
{
currentData.PropertyChanged += OnPropertyChanged;
}
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Property == Events.Changed)
{
var dataGridRow = AssociatedObject.TryFindParent<DataGridRow>();
//ColorAnimation.Completed += (s, x) =>
//{
// dataGridRow.Background = Brushes.Transparent;
//};
dataGridRow.Background =
(SolidColorBrush) new BrushConverter().ConvertFromString(dataGridRow.Background.ToString());
dataGridRow.Background?
.BeginAnimation(SolidColorBrush.ColorProperty, ColorAnimation);
}
}
protected override void OnDetaching()
{
if (AssociatedObject.DataContext is INotifyPropertyChanged currentData)
{
currentData.PropertyChanged -= OnPropertyChanged;
}
}
}
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastUpdated}">
<Interactivity:Interaction.Behaviors>
<customBehavior:AnimateBehavior Property="Changed" />
</Interactivity:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
My CustomControl changes the Visibility of it's children when i assign a value to a child. Here's my code
<UserControl x:Class="CrossProjectUserControls.controls.OnOffSwitch"
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:CrossProjectUserControls.controls"
mc:Ignorable="d" Background="White" Loaded="UserControl_Loaded">
<Grid>
<Image x:Name="imgSwitch" HorizontalAlignment="Left" MaxHeight="30" MinWidth="80" PreviewMouseLeftButtonDown="imgSwitch_PreviewMouseLeftButtonDown" Source="/CrossProjectUserControls;component/res/icons/onoffswitch/switch_off_1_V3_100x40.png" Margin="3,3,0,3"/>
<Label x:Name="lblText" Content="{Binding Text}" Margin="83,3,5,5" VerticalAlignment="Center" FontFamily="Arial" FontSize="16" Background="#FF633C3C"/>
</Grid>
public partial class OnOffSwitch : UserControl {
private BitmapImage bmpOn = new BitmapImage(new Uri(#"/MentorPlus;component/res/icons/onoffswitch/switch_off_1_V3_100x40.png", UriKind.Relative));
private BitmapImage bmpOff = new BitmapImage(new Uri(#"/MentorPlus;component/res/icons/onoffswitch/switch_on_1_V3_100x40.png", UriKind.Relative));
private bool isOn = false;
private string text = "Debug";
public bool IsOn
{
get { return isOn; }
set
{
isOn = value;
ReloadUI();
}
}
public string Text
{
get { return text; }
set { text = value; lblText.Content = text; }
}
public static readonly RoutedEvent SwitchEvent = EventManager.RegisterRoutedEvent(
"Switched", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(OnOffSwitch));
public OnOffSwitch() {
InitializeComponent();
}
private void InitializeControls() {
//this.lblText.Content = text;
//this.imgSwitch.Source = bmpOff;
}
public void Switch() {
IsOn = !IsOn;
}
private void ReloadUI() {
if (this.IsOn == true) {
this.imgSwitch.Source = bmpOn;
} else if (this.IsOn == false) {
this.imgSwitch.Source = bmpOff;
}
}
public event RoutedEventHandler Switched
{
add { AddHandler(SwitchEvent, value); }
remove { RemoveHandler(SwitchEvent, value); }
}
void RaiseSwitchEvent() {
RoutedEventArgs newEventArgs = new RoutedEventArgs(OnOffSwitch.SwitchEvent);
RaiseEvent(newEventArgs);
}
#region Events
private void imgSwitch_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
Switch();
RaiseSwitchEvent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
InitializeControls();
}
#endregion
}
So the Label is totally hidden if i assign a string to it's content like this.lblText.Content = "Test". Is there something I am missing or did I do something wrong?
Thanks for every help
I keep a list of current alarms and events in a basic datagrid, the user can scroll through the list. The user can select one item and acknowledge that specific alarm. This works.
The user should also be aloud to acknowledge all visible items. This is where im stuck. Is it possible to retrieve a list of currentcollectionshown from a datagrid?
Part of XAML
<Grid Background="DarkGray">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<Button Content="Ack visible" Command="{Binding AcknowledgeVisibleCommand}"/>
<Button Content="Ack selected" Command="{Binding AcknowledgeSelectedCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding AlarmAndEventList}" SelectedItem="{Binding SelectedAlarmItem}" Background="DarkGray" IsReadOnly="True">
</DataGrid>
</StackPanel>
</Grid>
Part of ViewModel
public ObservableCollection<AlarmItem> AlarmAndEventList { get { return _AlarmAndEventList; } }
private void AcknowledgeVisible()
{
//Do something with a list of visible items in datagrid
}
private void AcknowledgeSelected()
{
if (SelectedAlarmItem != null)
{
AlarmAndEventInstance.updateAlarmItem(SelectedAlarmItem);
}
}
Also, I want to run the single acknowledge command if the user double clicks an item. What is the "MVVM Way" of responding to a double click on a datagrid?
To observe the currently visible items in a DataGrid I have written the following attached-property:
public class DataGridExtensions
{
public static readonly DependencyProperty ObserveVisiblePersonsProperty = DependencyProperty.RegisterAttached(
"ObserveVisiblePersons", typeof(bool), typeof(DataGridExtensions),
new PropertyMetadata(false, OnObserveVisiblePersonsChanged));
public static readonly DependencyProperty VisiblePersonsProperty = DependencyProperty.RegisterAttached(
"VisiblePersons", typeof(List<Person>), typeof(DataGridExtensions),
new PropertyMetadata(null));
private static readonly DependencyProperty SenderDataGridProperty = DependencyProperty.RegisterAttached(
"SenderDataGrid", typeof(DataGrid), typeof(DataGridExtensions), new PropertyMetadata(default(DataGrid)));
private static void OnObserveVisiblePersonsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = d as DataGrid;
if (dataGrid == null)
return;
dataGrid.Loaded += DataGridLoaded;
}
private static void DataGridLoaded(object sender, RoutedEventArgs routedEventArgs)
{
DataGrid dataGrid = (DataGrid) sender;
dataGrid.Loaded -= DataGridLoaded;
ScrollViewer scrollViewer = FindChildren<ScrollViewer>(dataGrid).FirstOrDefault();
if (scrollViewer != null)
{
SetSenderDataGrid(scrollViewer, dataGrid);
scrollViewer.ScrollChanged += ScrollViewerOnScrollChanged;
}
}
public static void SetObserveVisiblePersons(DependencyObject element, bool value)
{
element.SetValue(ObserveVisiblePersonsProperty, value);
}
public static bool GetObserveVisiblePersons(DependencyObject element)
{
return (bool) element.GetValue(ObserveVisiblePersonsProperty);
}
private static void SetSenderDataGrid(DependencyObject element, DataGrid value)
{
element.SetValue(SenderDataGridProperty, value);
}
private static DataGrid GetSenderDataGrid(DependencyObject element)
{
return (DataGrid) element.GetValue(SenderDataGridProperty);
}
private static void ScrollViewerOnScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
if (scrollViewer == null)
return;
ScrollBar verticalScrollBar =
FindChildren<ScrollBar>(scrollViewer).FirstOrDefault(s => s.Orientation == Orientation.Vertical);
if (verticalScrollBar != null)
{
DataGrid dataGrid = GetSenderDataGrid(scrollViewer);
int totalCount = dataGrid.Items.Count;
int firstVisible = (int) verticalScrollBar.Value;
int lastVisible = (int) (firstVisible + totalCount - verticalScrollBar.Maximum);
List<Person> visiblePersons = new List<Person>();
for (int i = firstVisible; i <= lastVisible; i++)
{
visiblePersons.Add((Person) dataGrid.Items[i]);
}
SetVisiblePersons(dataGrid, visiblePersons);
}
}
public static void SetVisiblePersons(DependencyObject element, List<Person> value)
{
element.SetValue(VisiblePersonsProperty, value);
}
public static List<Person> GetVisiblePersons(DependencyObject element)
{
return (List<Person>) element.GetValue(VisiblePersonsProperty);
}
private static IList<T> FindChildren<T>(DependencyObject element) where T : FrameworkElement
{
List<T> retval = new List<T>();
for (int counter = 0; counter < VisualTreeHelper.GetChildrenCount(element); counter++)
{
FrameworkElement toadd = VisualTreeHelper.GetChild(element, counter) as FrameworkElement;
if (toadd != null)
{
T correctlyTyped = toadd as T;
if (correctlyTyped != null)
{
retval.Add(correctlyTyped);
}
else
{
retval.AddRange(FindChildren<T>(toadd));
}
}
}
return retval;
}
}
And in the xaml in the definition of your DataGrid you have to write the following:
nameSpaceOfAttachedProperty:DataGridExtensions.ObserveVisiblePersons="True"
nameSpaceOfAttachedProperty:DataGridExtensions.VisiblePersons="{Binding VisiblePersons, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And in your ViewModel you will have a property like:
private List<Person> visiblePersons;
public List<Person> VisiblePersons
{
get { return visiblePersons; }
set
{
visiblePersons = value;
OnPropertyChanged();
}
}
And every time you scroll the VisiblePersons will be updated.
In your case you have to changed the type of the List inside the attached-propertay to match your requirements.
You can use MyDataGrid.Items collection to filter out.
You can also apply filtering in your MyDataGrid. But don't show filtered items. How to filter DataGrid