I've built my own control to mimic the VariableSizedGridView in Windows 8. Because that control does not support UI virtualization, I rebuilt it with virtualization. The control basically measures all the elements and calculates their positions, but only draws those that are in view. This makes for an enormous performance boost on the Surface RT.
There is one problem though, the control leaks memory. I'm sure that it's the control (or something related to it), as I've been able to reproduce the leak in an isolated app. The source code of the control is split in two classes. The VirtualizedList class is the base of the control, and defines a couple of DependencyProperties. The VirtualizedVariableSizedWrapGrid class is where the magic happens and where the items are being drawn on the screen.
I'm not sure how I can fix this issue, as it's been happening for months now, and nothing I did helped. I checked my events, tried disposing the UI elements (which is unpossible, it seems). I guess the leak happens because some child elements of the DataTemplate are still bound to the data object, and thus still reference to them, and that's why the UI objects still stay in memory. I could be completely wrong though. As you can see in the code, when I update the view, I commented out RemoveChild(old);. That's because when I reuse old FrameworkElements (created from the DataTemplate), the leak goes slower. When I recreate new items all the time, the leak is about 5MB per scroll operation.
Any help on this would be very much appreciated! Thank you.
PS: Seems like I can not attach all source code. The VirtualizedList class only defines some DependencyProperties, so I'll leave that one out.
Source
VirtualizedVariableSizedWrapGrid
[TemplatePart(Name="Root", Type=typeof(Grid))]
[TemplatePart(Name="Scroll", Type=typeof(ScrollViewer))]
[TemplatePart(Name="LayoutArea", Type=typeof(Grid))]
[TemplatePart(Name="SnappedView", Type=typeof(ListView))]
public class VirtualizedVariableSizedWrapGrid : VirtualizedList
{
public static readonly DependencyProperty SnappedItemContainerStyleProperty = DependencyProperty.Register("SnappedItemContainerStyle", typeof(Style), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(null, SnappedItemContainerStyleChanged));
public static readonly DependencyProperty ListStateProperty = DependencyProperty.Register("ListState", typeof(ViewState), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(ViewState.Full, ListStateChanged));
private readonly List<object> _currentDataView;
private Grid _root;
private Panel _panel;
private ScrollViewer _scrollViewer;
private ListView _snappedView;
private const double COLUMNS_PRELOADED = 2;
private const double CARD_MARGIN = 6.0;
private int _maxRows = 0;
private object _selectedItem;
private bool _moreDataRequested = false;
public delegate void CalculatingItemSizeEventHandler(ItemContainer item);
public event CalculatingItemSizeEventHandler OnCalculatingItemSize;
public event SelectionChangedEventHandler SelectionChanged;
public event EventHandler DataRequested;
public enum ViewState
{
Full,
Snapped
}
public VirtualizedVariableSizedWrapGrid()
{
_currentDataView = new List<object>();
DefaultStyleKey = typeof(VirtualizedVariableSizedWrapGrid);
this.Style = (Style) XamlReader.Load(
#"<Style xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:controls=""using:MeTweets.Controls"" TargetType=""controls:VirtualizedVariableSizedWrapGrid"">
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""controls:VirtualizedVariableSizedWrapGrid"">
<Grid x:Name=""Root"" VerticalAlignment=""Stretch"" HorizontalAlignment=""Stretch"">
<ScrollViewer x:Name=""Scroll"" ZoomMode=""Disabled"" HorizontalScrollMode=""Auto"" VerticalScrollMode=""Disabled"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden"" HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"">
<Grid x:Name=""LayoutArea"" />
</ScrollViewer>
<ListView x:Name=""SnappedView"" HorizontalContentAlignment=""Stretch"" HorizontalAlignment=""Stretch"" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>");
this.Loaded += VirtualizedVariableSizedWrapGrid_Loaded;
}
~VirtualizedVariableSizedWrapGrid()
{
this.Loaded -= VirtualizedVariableSizedWrapGrid_Loaded;
}
#region Dependency Property Changed
private static void SnappedItemContainerStyleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid;
if (varSizeGrid == null || !(e.NewValue is Style))
return;
varSizeGrid.SetupSnappedView();
}
private static void ListStateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid;
if (varSizeGrid == null || e.NewValue.Equals(e.OldValue))
return;
if (varSizeGrid.ListState == ViewState.Full)
{
varSizeGrid._scrollViewer.Visibility = Visibility.Visible;
varSizeGrid._snappedView.Visibility = Visibility.Collapsed;
}
else if (varSizeGrid.ListState == ViewState.Snapped)
{
varSizeGrid._scrollViewer.Visibility = Visibility.Collapsed;
varSizeGrid._snappedView.Visibility = Visibility.Visible;
}
if (varSizeGrid.ListState == ViewState.Full)
{
var snapScroll = varSizeGrid._snappedView.FindFirstChildOfType<ScrollViewer>();
if (snapScroll != null)
{
int index = (int)snapScroll.VerticalOffset;
if (index >= 0 && index < varSizeGrid.ItemsSource.Count)
varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[index]);
}
}
else if (varSizeGrid.ListState == ViewState.Snapped)
{
if (varSizeGrid._itemContainer == null)
return;
var item = (from a in varSizeGrid._itemContainer
where (a.Column * varSizeGrid.ItemWidth) + varSizeGrid.ItemWidth > varSizeGrid._scrollViewer.HorizontalOffset + varSizeGrid.Padding.Left
select a).FirstOrDefault();
if (item == null)
return;
varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[item.Index]);
}
}
#endregion
#region Virtual Voids
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Item template changed");
SetupSnappedView();
GenerateItemContainers();
}
protected override void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Items source changed");
SetupSnappedView();
if (e.OldValue != null)
{
if (e.OldValue is INotifyCollectionChanged)
{
INotifyCollectionChanged collection = (INotifyCollectionChanged)e.OldValue;
collection.CollectionChanged -= collection_CollectionChanged;
}
ClearItems();
}
if (e.NewValue != null)
{
GenerateItemContainers();
UpdateView();
if (e.NewValue is INotifyCollectionChanged)
{
INotifyCollectionChanged collection = (INotifyCollectionChanged)e.NewValue;
collection.CollectionChanged += collection_CollectionChanged;
}
}
}
protected virtual DataTemplate GetDataTemplateForItem(object item)
{
return ItemTemplate;
}
#endregion
#region Properties
public Style SnappedItemContainerStyle
{
get { return (Style)GetValue(SnappedItemContainerStyleProperty); }
set { SetValue(SnappedItemContainerStyleProperty, value); }
}
public ViewState ListState
{
get { return (ViewState)GetValue(ListStateProperty); }
set { SetValue(ListStateProperty, value); }
}
public object SelectedValue
{
get { return _selectedItem; }
set
{
if (value == _selectedItem)
return;
if (value == null)
{
_selectedItem = null;
return;
}
var oldValue = _selectedItem;
if (BringIntoView(value))
{
_selectedItem = value;
if (SelectionChanged != null)
SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { oldValue }, new List<object>() { value }));
}
}
}
#endregion
#region Overrides
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
Debug.WriteLine("OnApplyTemplate");
_root = this.GetTemplateChild("Root") as Grid;
_panel = this.GetTemplateChild("LayoutArea") as Panel;
_scrollViewer = this.GetTemplateChild("Scroll") as ScrollViewer;
_snappedView = this.GetTemplateChild("SnappedView") as ListView;
_snappedView.Loaded += _snappedView_Loaded;
_scrollViewer.ViewChanging += _scrollViewer_ViewChanging;
_scrollViewer.ViewChanged += _scrollViewer_ViewChanged;
_panel.SizeChanged += _panel_SizeChanged;
_snappedView.SelectionChanged += _snappedView_SelectionChanged;
App.RootFrame.SizeChanged += RootFrame_SizeChanged;
if (ItemsSource != null)
{
GenerateItemContainers();
UpdateView();
}
SetupSnappedView();
}
#endregion
#region Private voids
protected override void ClearItems()
{
if (_panel == null)
return;
foreach (var item in _panel.Children.Cast<FrameworkElement>())
{
RemoveChild(item);
}
_currentDataView.Clear();
_panel.Children.Clear();
}
protected override void GenerateItemContainers()
{
if (_panel == null || _panel.Visibility == Visibility.Collapsed || _panel.ActualHeight == 0 || _panel.ActualWidth == 0)
return;
_itemContainer.Clear();
if (ItemsSource == null)
return;
int _currentRow = 0, _currentColumn = 0;
int _currentColWidth = 1;
_maxRows = (int)((_panel.ActualHeight-this.Padding.Bottom-this.Padding.Top) / this.ItemHeight);
for (int ix = 0; ix < ItemsSource.Count; ix++)
{
var item = ItemsSource[ix];
var container = new ItemContainer { Index = ix };
if (_currentDataView.Contains(item))
container.IsRealized = true;
if (OnCalculatingItemSize != null)
OnCalculatingItemSize(container);
if (container.RowSpan < 0)
container.RowSpan = 0;
if (container.ColumnSpan < 0)
container.ColumnSpan = 0;
container.Column = _currentColumn;
container.Row = _currentRow;
if (container.RowSpan + _currentRow > _maxRows) //Not enough rows --> new column
{
if (_itemContainer.Count > 0)
{
// Equally split the rest of the space in this column over the elements that are currently in it
var additionalrows = _maxRows - _currentRow;
var c = _itemContainer.Where(x => x.Column == _currentColumn).ToList();
var ccount = c.Count();
var i = 0;
foreach (var t in c)
{
t.Row += (additionalrows/ccount)*i;
t.RowSpan += additionalrows/ccount;
i++;
}
// Couldn't equally divide, give the rest of the space to the last element
if (additionalrows%ccount > 0)
{
c.Last().RowSpan += additionalrows%ccount;
}
}
_currentColumn++;
container.Column = _currentColumn;
_currentRow = 0;
container.Row = _currentRow;
_currentColWidth = container.ColumnSpan;
_itemContainer.Add(container);
_currentRow += container.RowSpan;
continue;
}
//Make column as wide as widest element
if (container.ColumnSpan > _currentColWidth)
_currentColWidth = container.ColumnSpan;
if (_currentColWidth > container.ColumnSpan)
container.ColumnSpan = _currentColWidth;
_currentRow += container.RowSpan;
if (_currentRow >= _maxRows)
{
//Make sure all elements in column are as wide as widest element
foreach (var wider in
_itemContainer.Where(x => x.ColumnSpan < _currentColWidth && x.Column == _currentColumn))
{
wider.ColumnSpan = _currentColWidth;
}
_currentRow = 0;
_currentColumn += _currentColWidth;
_currentColWidth = 1;
}
_itemContainer.Add(container);
}
}
protected override void UpdateView()
{
if (_panel == null || _scrollViewer == null || _itemContainer == null || _itemContainer.Count == 0)
return;
_panel.Width = ((_itemContainer.Last().Column + 1) * this.ItemWidth) + this.Padding.Left + this.Padding.Right;
var margin = this.ItemWidth * COLUMNS_PRELOADED;
double minLeft = (_scrollViewer.HorizontalOffset - margin);
double maxLeft = (_scrollViewer.HorizontalOffset + _scrollViewer.ActualWidth + margin);
int minColumn = (int)(minLeft / this.ItemWidth)-1;
int maxColumn = (int)(maxLeft / this.ItemWidth)+1;
minLeft = (minColumn * this.ItemWidth) + this.Padding.Left;
maxLeft = (maxColumn * this.ItemWidth) + this.Padding.Left;
_currentDataView.Clear();
var shown = _itemContainer.Where(x => x.Column >= minColumn && x.Column <= maxColumn);
_currentDataView.AddRange(shown.Select(x => ItemsSource[x.Index]));
List<FrameworkElement> recycle = new List<FrameworkElement>();
foreach (var old in _panel.Children.Cast<FrameworkElement>().Where(x => x.Margin.Left < minLeft || x.Margin.Left > maxLeft ||
!(_currentDataView.Contains(x.DataContext))).ToList())
{
foreach (var recycled in _itemContainer.Where(x => ItemsSource[x.Index] == old.DataContext))
recycled.IsRealized = false;
//RemoveChild(old);
recycle.Add(old);
}
foreach (var item in shown)
{
FrameworkElement obj = recycle.FirstOrDefault();
double left, top;
if (item.IsRealized)
{
//Rearrange item if needed
obj = _panel.Children.FirstOrDefault(x => x is FrameworkElement && ((FrameworkElement)x).DataContext == ItemsSource[item.Index]) as FrameworkElement;
if (obj == null)
continue;
obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN);
obj.Width = (this.ItemWidth * item.ColumnSpan) - (2 * CARD_MARGIN);
left = (item.Column * this.ItemWidth) + this.Padding.Left;
top = (item.Row * this.ItemHeight) + this.Padding.Top;
obj.Margin = new Thickness(left, top, 0, 0);
continue;
}
item.IsRealized = true;
if (obj == null)
{
obj = this.GetDataTemplateForItem(item).LoadContent() as FrameworkElement;
obj.Tapped += Item_Tapped;
obj.PointerReleased += Item_PointerReleased;
obj.PointerEntered += Item_PointerEntered;
obj.PointerExited += Item_PointerExited;
_panel.Children.Add(obj);
}
else
recycle.Remove(obj);
obj.DataContext = null;
obj.DataContext = ItemsSource[item.Index];
obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN);
obj.Width = (this.ItemWidth * item.ColumnSpan) - (2 * CARD_MARGIN);
obj.VerticalAlignment = VerticalAlignment.Top;
obj.HorizontalAlignment = HorizontalAlignment.Left;
obj.Visibility = Visibility.Visible;
left = (item.Column * this.ItemWidth) + this.Padding.Left;
top = (item.Row * this.ItemHeight) + this.Padding.Top;
obj.Margin = new Thickness(left, top, 0, 0);
}
foreach (var notRecycled in recycle) //Remove not recycled items
RemoveChild(notRecycled);
Debug.WriteLine("Children count "+_panel.Children.Count);
}
private void RemoveChild(FrameworkElement item)
{
item.Tapped -= Item_Tapped;
item.PointerReleased -= Item_PointerReleased;
item.PointerEntered -= Item_PointerEntered;
item.PointerExited -= Item_PointerExited;
_panel.Children.Remove(item);
item.DataContext = null;
item.ClearValue(DataContextProperty);
}
public static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type)
{
var list = type.DeclaredProperties.ToList();
var subtype = type.BaseType;
if (subtype != null)
list.AddRange(GetAllProperties(subtype.GetTypeInfo()));
return list.ToArray();
}
public List<DependencyObject> AllChildren(DependencyObject parent)
{
var list = new List<DependencyObject>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child != null)
list.Add(child);
list.AddRange(AllChildren(child));
}
return list;
}
private void SetupSnappedView()
{
if (_snappedView == null)
return;
_snappedView.ItemTemplate = this.ItemTemplate;
_snappedView.ItemContainerStyle = this.SnappedItemContainerStyle;
_snappedView.ItemsSource = this.ItemsSource;
var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>();
if (snapScroll != null)
{
snapScroll.ViewChanged -= snapped_ViewChanged;
snapScroll.ViewChanged += snapped_ViewChanged;
}
}
private void Item_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
}
private void Item_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
}
private bool _pointerPressed = false;
private object _pointerOriginalSource = null;
private void Item_PointerReleased(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (e.Handled)
return;
_pointerPressed = true;
_pointerOriginalSource = e.OriginalSource;
}
private void Item_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
if (!_pointerPressed || e.OriginalSource != _pointerOriginalSource)
{
_pointerPressed = false;
return;
}
object previousObject = _selectedItem;
object dataObject = ((FrameworkElement)sender).DataContext;
if (dataObject != _selectedItem)
{
_selectedItem = dataObject;
if (SelectionChanged != null)
SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { previousObject }, new List<object>() { dataObject }));
}
_pointerPressed = false;
}
void collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
object item = null;
if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Snapped)
{
var snapScroll = _snappedView.FindFirstChildOfType<ScrollViewer>();
if (snapScroll != null)
{
int index = (int)snapScroll.VerticalOffset;
if (index >= 0 && index < ItemsSource.Count)
item = ItemsSource[index];
}
}
else if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Full)
{
var i = (from a in _itemContainer
where (a.Column * ItemWidth) + ItemWidth > _scrollViewer.HorizontalOffset + Padding.Left
select a).FirstOrDefault();
if (i == null)
return;
item = ItemsSource[i.Index];
}
if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Reset)
_moreDataRequested = false;
GenerateItemContainers();
UpdateView();
//if (item != null)
// BringIntoView(item);
//else if (this.ItemsSource is IList && (this.ItemsSource as IList).Count > 0)
// BringIntoView((this.ItemsSource as IList)[0]);
}
void VirtualizedVariableSizedWrapGrid_Loaded(object sender, RoutedEventArgs e)
{
if (this.ListState == ViewState.Full)
{
this._scrollViewer.Visibility = Visibility.Visible;
this._snappedView.Visibility = Visibility.Collapsed;
}
else if (this.ListState == ViewState.Snapped)
{
this._scrollViewer.Visibility = Visibility.Collapsed;
this._snappedView.Visibility = Visibility.Visible;
}
Debug.WriteLine("VirtualizedVariableSizedWrapGrid Loaded");
if (this.ItemsSource != null && (_itemContainer == null || _itemContainer.Count == 0))
{
GenerateItemContainers();
UpdateView();
}
}
void snapped_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!(sender is ScrollViewer))
return;
var scroll = (sender as ScrollViewer);
var maxOffset = scroll.ExtentHeight - scroll.ViewportHeight;
if (!_moreDataRequested && (maxOffset <= 0 || scroll.VerticalOffset >= maxOffset))
{
_moreDataRequested = true;
if (DataRequested != null)
DataRequested(this, EventArgs.Empty);
}
}
void _snappedView_Loaded(object sender, RoutedEventArgs e)
{
var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>();
if (snapScroll != null)
{
snapScroll.ViewChanged += snapped_ViewChanged;
}
}
private double _lastOffsetUpdate = 0;
void _scrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
if (e.NextView.HorizontalOffset == e.FinalView.HorizontalOffset)
{
UpdateView();
return;
}
if (Math.Abs(e.NextView.HorizontalOffset - _lastOffsetUpdate) > 100)
{
_lastOffsetUpdate = e.NextView.HorizontalOffset;
UpdateView();
}
}
void _scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!_moreDataRequested && this._scrollViewer.HorizontalOffset + this._scrollViewer.ActualWidth >= (this._panel.ActualWidth))
{
_moreDataRequested = true;
if (DataRequested != null)
DataRequested(this, EventArgs.Empty);
}
}
void _panel_SizeChanged(object sender, SizeChangedEventArgs e)
{
int currentMaxRows = _maxRows;
_maxRows = (int)((_panel.ActualHeight - this.Padding.Bottom - this.Padding.Top) / this.ItemHeight);
if (_maxRows != currentMaxRows) //Orientation probably changed, control got higher
{
Debug.WriteLine("Panel size changed");
ClearItems();
GenerateItemContainers();
UpdateView();
}
}
void _snappedView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
this.SelectedValue = e.AddedItems[0];
_snappedView.SelectedItem = null;
}
void RootFrame_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (_itemContainer != null && _itemContainer.Count > 0 && _panel.ActualWidth > 0)
UpdateView();
}
#endregion
#region Public voids
public override bool BringIntoView(object obj, bool animate = true)
{
try
{
if (_itemContainer == null || _itemContainer.Count == 0)
return false;
var item = (from a in _itemContainer
where ItemsSource[a.Index] == obj
select a).FirstOrDefault();
if (item == null)
return false;
//Calculate position
double offset = (item.Column * this.ItemWidth) + CARD_MARGIN;
this._scrollViewer.ChangeView(offset, null, null, !animate);
_snappedView.ScrollIntoView(obj, ScrollIntoViewAlignment.Leading);
UpdateView();
return true;
}
catch { return false; }
}
#endregion
}
Do you set ContentTemplate and/or Style for containers somewhere? Or maybe another properties such as BorderBrush etc? Maybe you do that in xaml?
If you do, try to set all these properties to null when releasing the container. It helped me in some memory leak cases.
Related
This is a link to the winforms project from my onedrive:
https://1drv.ms/u/s!AmVw2eqP6FhB4h01pNXIMlqdZJk_?e=wSGaRx
using Newtonsoft.Json;
using Ookii.Dialogs.WinForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window;
namespace Image_Crop
{
public partial class Form1 : Form
{
Rectangle rect;
int pixelsCounter = 0;
Color SelectedColor = Color.LightGreen;
List<DrawingRectangle> DrawingRects = new List<DrawingRectangle>();
Bitmap rectImage;
int saveRectanglesCounter = 1;
bool drawBorder = true;
bool clearRectangles = true;
bool saveRectangles = true;
//bool drawnNewRectangle = false; // To think how to work with this flag bool variable
// where to use it how and how to solce this problem !!!!!
string rectangleName;
Dictionary<string, string> FileList = new Dictionary<string, string>();
string selectedPath;
int x, y;
private bool crop = false;
public Form1()
{
InitializeComponent();
textBox1.Text = Properties.Settings.Default.ImageToCropFolder;
textBox2.Text = Properties.Settings.Default.CroppedImagesFolder;
selectedPath = textBox2.Text;
if (textBox1.Text != "")
{
Bitmap bmp = new Bitmap(Image.FromFile(textBox1.Text),
pictureBox2.Width, pictureBox2.Height);
pictureBox2.Image = bmp;
}
checkBoxDrawBorder.Checked = true;
checkBoxClearRectangles.Checked = true;
checkBoxSaveRectangles.Checked = true;
if (selectedPath != "" && selectedPath != null)
{
if (System.IO.File.Exists(Path.Combine(selectedPath, "rectangles.txt")))
{
string g = System.IO.File.ReadAllText(Path.Combine(selectedPath, "rectangles.txt"));
g = g.Remove(0, 32);
FileList = JsonConvert.DeserializeObject<Dictionary<string, string>>(g);
listBox1.DataSource = FileList.Keys.ToList();
label2.Text = listBox1.Items.Count.ToString();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
else
{
label2.Text = "0";
}
}
else
{
label2.Text = "0";
}
if ((selectedPath != "" && selectedPath != null) && textBox1.Text != "")
{
crop = true;
}
else
{
crop = false;
}
}
public class DrawingRectangle
{
public Rectangle Rect => new Rectangle(Location, Size);
public Size Size { get; set; }
public Point Location { get; set; }
public Control Owner { get; set; }
public Point StartPosition { get; set; }
public Color DrawingcColor { get; set; } = Color.LightGreen;
public float PenSize { get; set; } = 3f;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || crop == false) return;
x = 0;
y = 0;
if (pictureBox2.Image != null && selectedPath != null)
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
DrawingRects.Add(new DrawingRectangle()
{
Location = e.Location,
Size = Size.Empty,
StartPosition = e.Location,
Owner = (Control)sender,
DrawingcColor = SelectedColor
});
}
}
}
private void pictureBox2_MouseMove(object sender, MouseEventArgs e)
{
int X = e.X;
int Y = e.Y;
if (e.Button != MouseButtons.Left || crop == false) return;
if ((X >= 0 && X <= pictureBox2.Width) && (Y >= 0 && Y <= pictureBox2.Height))
{
if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0)
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
x = e.X;
y = e.Y;
var dr = DrawingRects[DrawingRects.Count - 1];
if (e.Y < dr.StartPosition.Y) { dr.Location = new Point(dr.Rect.Location.X, e.Y); }
if (e.X < dr.StartPosition.X) { dr.Location = new Point(e.X, dr.Rect.Location.Y); }
dr.Size = new Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y));
pictureBox2.Invalidate();
}
}
}
}
int count = 0;
private void pictureBox2_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || crop == false) return;
if (DrawingRects.Count > 0 && pictureBox2.Image != null && selectedPath != "")
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
var dr = DrawingRects.Last();
if (dr.Rect.Width > 0 && dr.Rect.Height > 0)
{
rectImage = cropAtRect((Bitmap)pictureBox2.Image, dr.Rect);
if (saveRectangles)
{
count++;
rectangleName = GetNextName(Path.Combine(selectedPath, "Rectangle"), ".bmp");
FileList.Add($"{dr.Location}, {dr.Size}", rectangleName);
string json = JsonConvert.SerializeObject(
FileList,
Formatting.Indented
);
using (StreamWriter sw = new StreamWriter(Path.Combine(selectedPath, "rectangles.txt"), false))
{
sw.WriteLine("Total number of rectangles: " + count + Environment.NewLine);
sw.Write(json);
sw.Close();
}
rectImage.Save(rectangleName);
saveRectanglesCounter++;
}
pixelsCounter = rect.Width * rect.Height;
pictureBox1.Invalidate();
listBox1.DataSource = FileList.Keys.ToList();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
pictureBox2.Focus();
Graphics g = Graphics.FromImage(this.pictureBox1.Image);
g.Clear(this.pictureBox1.BackColor);
}
}
else
{
if (clearRectangles)
{
DrawingRects.Clear();
pictureBox2.Invalidate();
}
x = 0;
y = 0;
}
}
}
string GetNextName(string baseName, string extension)
{
int counter = 1;
string nextName = baseName + counter + extension;
while (System.IO.File.Exists(nextName))
{
counter++;
nextName = baseName + counter + extension;
}
return nextName;
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
if (drawBorder)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox2.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0)
{
DrawShapes(e.Graphics);
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (drawBorder)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
if (rectImage != null && DrawingRects.Count > 0)
{
var dr = DrawingRects.Last();
e.Graphics.DrawImage(rectImage, dr.Rect);
if (clearRectangles)
{
DrawingRects.Clear();
pictureBox2.Invalidate();
}
}
}
private void DrawShapes(Graphics g)
{
if (DrawingRects.Count == 0) return;
g.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var dr in DrawingRects)
{
if (dr.Rect.Width > 0 && dr.Rect.Height > 0)
{
using (Pen pen = new Pen(dr.DrawingcColor, dr.PenSize))
{
g.DrawRectangle(pen, dr.Rect);
};
}
}
}
public Bitmap cropAtRect(Bitmap b, Rectangle r)
{
Bitmap nb = new Bitmap(r.Width, r.Height);
using (Graphics g = Graphics.FromImage(nb))
{
g.DrawImage(b, -r.X, -r.Y);
return nb;
}
}
private void checkBoxDrawBorder_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxDrawBorder.Checked)
{
drawBorder = true;
}
else
{
drawBorder = false;
}
pictureBox1.Invalidate();
pictureBox2.Invalidate();
}
private void checkBoxClearRectangles_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxClearRectangles.Checked)
{
clearRectangles = true;
}
else
{
clearRectangles = false;
}
pictureBox2.Invalidate();
}
private void checkBoxSaveRectangles_CheckedChanged(object sender, EventArgs e)
{
if(checkBoxSaveRectangles.Checked)
{
saveRectangles = true;
}
else
{
saveRectangles = false;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var item = ((ListBox)sender).SelectedItem;
var val = FileList[(string)item];
pictureBox1.Image = System.Drawing.Image.FromFile(val);
}
private void button1_Click(object sender, EventArgs e)
{
VistaOpenFileDialog dialog = new VistaOpenFileDialog();
{
dialog.Filter = "Images (*.jpg, *.bmp, *.gif)|*.jpg;*.bmp;*.gif";
};
if (dialog.ShowDialog() == DialogResult.OK)
{
textBox1.Text = dialog.FileName;
Properties.Settings.Default.ImageToCropFolder = dialog.FileName;
Properties.Settings.Default.Save();
Bitmap bmp = new Bitmap(Image.FromFile(dialog.FileName),
pictureBox2.Width, pictureBox2.Height);
pictureBox2.Image = bmp;
if(textBox1.Text != "" && textBox2.Text != "")
{
crop = true;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
textBox2.Text = dialog.SelectedPath;
selectedPath = dialog.SelectedPath;
Properties.Settings.Default.CroppedImagesFolder = selectedPath;
Properties.Settings.Default.Save();
if (textBox1.Text != "" && textBox2.Text != "")
{
crop = true;
}
}
}
}
}
The problem is in the mouse up event i can't find the logic with the saving flag saveRectangles.
this is the saving part in the mouse up
if (saveRectangles)
{
count++;
rectangleName = GetNextName(Path.Combine(selectedPath, "Rectangle"), ".bmp");
FileList.Add($"{dr.Location}, {dr.Size}", rectangleName);
string json = JsonConvert.SerializeObject(
FileList,
Formatting.Indented
);
using (StreamWriter sw = new StreamWriter(Path.Combine(selectedPath, "rectangles.txt"), false))
{
sw.WriteLine("Total number of rectangles: " + count + Environment.NewLine);
sw.Write(json);
sw.Close();
}
rectImage.Save(rectangleName);
saveRectanglesCounter++;
}
if the checkBox checkBoxSaveRectangles is not checked when running the application and the folder in textBox1 is empty there are no saved images yet on the hard disk then it will throw exception at the line 201 in the MouseUp event :
Graphics g = Graphics.FromImage(this.pictureBox1.Image);
because the image in pictureBox1 is null.
but in that case i still want it to crop images and add the images information as items to the listBox just not to save it to the hard disk.
i think that rectImage variable is also used in the pictureBox1 paint event.
anyway the goal is to be able to keep cropping even if the saving checkbox not checked and the there are no saved images in the cropped images folder.
I have 4 pictureBoxes. I need they move until hit right side and then move to left side and again. But after 1st picturebox hit left side other move closer to him. How fix it ??
link on video with problem
int changePositionX;
bool change = true;
private void timer1_Tick(object sender, EventArgs e)
{
foreach (Control x in this.Controls)
{
if (x is PictureBox && x.Tag != null && x.Tag.ToString() == "enemy")
{
if (x.Location.X < 750 && change == true)
{
changePositionX = x.Location.X + 50;
x.Location = new Point(changePositionX, x.Location.Y);
}
else
{
change = false;
}
if(x.Location.X >= 100 && change == false)
{
changePositionX = x.Location.X - 50;
x.Location = new Point(changePositionX, x.Location.Y);
}
else
{
change = true;
}
}
}
}
Try it like this instead:
private int jumpDistance = 50;
private bool goingRight = true;
private List<PictureBox> PBs = new List<PictureBox>();
private void Form1_Load(object sender, EventArgs e)
{
foreach (PictureBox pb in this.Controls.OfType<PictureBox>())
{
if (pb.Tag != null && pb.Tag.ToString() == "enemy")
{
PBs.Add(pb);
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (goingRight)
{
if (PBs.Any(pb => pb.Location.X >= 750))
{
goingRight = !goingRight;
}
}
else
{
if (PBs.Any(pb => pb.Location.X < 100))
{
goingRight = !goingRight; ;
}
}
foreach (PictureBox pb in PBs)
{
if (goingRight)
{
pb.Location = new Point(pb.Location.X + jumpDistance, pb.Location.Y);
}
else // going left
{
pb.Location = new Point(pb.Location.X - jumpDistance, pb.Location.Y);
}
}
}
Here's my code doing its thing with four PictureBoxes:
I am trying to perform a drag and drop in a list view. Problem is when I select an item with touch and move it. So firstly the TouchDown event executes which works fine. The e.OriginalSource for TouchEventArgs is a border when I keep a break point, I am able to find the parent as a listviewitem using FindParent method.
Problem is when the debugger comes to TouchMove Event, the TouchEventArgs, e.OriginalSource is being changed to ScrollViewer. I don't know how its changed. As a result the find parent returns null instead of the listviewitem. Please help.
Please see the TouchDown and TouchMove conditions in the GlobalHandler event in the code behind.
xaml:
<ListView
Name="RunSetupListView"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Style="{StaticResource RunSetupDisplayListViewStyle}"
ItemsSource="{Binding RunSetupInfoList}"
AlternationCount="100"
LayoutUpdated="RunSetupListView_LayoutUpdated"
PreviewMouseLeftButtonDown="GlobalHandler"
PreviewTouchDown="GlobalHandler"
PreviewMouseLeftButtonUp="GlobalHandler"
PreviewTouchUp="GlobalHandler"
PreviewMouseMove="GlobalHandler"
PreviewTouchMove="GlobalHandler"
DragEnter="RunSetupListView_DragEnter"
DragOver="RunSetupListView_DragOver"
Drop="RunSetupListView_Drop"
DragLeave="RunSetupListView_DragLeave"/>
Code Behind
private void GlobalHandler(object sender, InputEventArgs e)
{
if (e.RoutedEvent == PreviewMouseLeftButtonDownEvent)
{
var args = e as MouseButtonEventArgs;
var listViewItem =
VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject)args.OriginalSource);
if (listViewItem != null)
{
SelectedDragItem = listViewItem;
// Log start point
StartPoint = args.GetPosition(null);
}
}
if (e.RoutedEvent == PreviewTouchDownEvent)
{
var args = e as TouchEventArgs;
var listViewItem =
VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject)args.OriginalSource);
if (listViewItem != null)
{
SelectedDragItem = listViewItem;
// Log start point
StartPoint = args.GetTouchPoint(null).Position;
IsScreenTouched = true;
}
}
if (e.RoutedEvent == PreviewMouseLeftButtonUpEvent)
{
// Reest in case only a click occurs in the tree view WITHOUT a drag event
StartPoint = null;
SelectedDragItem = null;
}
if (e.RoutedEvent == PreviewTouchUpEvent)
{
// Reest in case only a click occurs in the tree view WITHOUT a drag event
StartPoint = null;
SelectedDragItem = null;
IsScreenTouched = false;
}
if (e.RoutedEvent == PreviewMouseMoveEvent)
{
var args = e as MouseEventArgs;
if (!(sender is ListView))
return;
// only for left button down and if we received a mouse down
// event in the listview... sometimes this event will still get processed
// even if you click outside the listview but then drag the mouse into the listview
if (args.LeftButton == MouseButtonState.Pressed && StartPoint != null && SelectedDragItem != null)
{
var mousePos = args.GetPosition(null);
var diff = StartPoint.Value - mousePos;
// Once the drag has been dragged far enough... prevents a drag from happening
// from simply clicking the item
if ((Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance) &&
!_inDragMode)
{
var listView = sender as ListView;
var listViewItem =
VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject)args.OriginalSource);
if (listViewItem == null)
{
return;
}
m_AdornerLayer = InitializeAdornerLayer(listViewItem, listView);
UpdateDragAdornerLocation(args.GetPosition(listView));
mousePoint = mousePos;
_inDragMode = true;
DataObject dragData = new DataObject(listViewItem.Content as DNA2RunInfoDisplay);
listView.CaptureMouse();
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
_inDragMode = false;
// Reset this here... the mouse up event DOES NOT get raised if we do a drag/drop effect
StartPoint = null;
SelectedDragItem = null;
}
}
}
if (e.RoutedEvent == PreviewTouchMoveEvent)
{
var args = e as TouchEventArgs;
if (!(sender is ListView))
return;
// only for left button down and if we received a mouse down
// event in the listview... sometimes this event will still get processed
// even if you click outside the listview but then drag the mouse into the listview
if (IsScreenTouched && StartPoint != null && SelectedDragItem != null)
{
var mousePos = args.GetTouchPoint(null).Position;
var diff = StartPoint.Value - mousePos;
// Once the drag has been dragged far enough... prevents a drag from happening
// from simply clicking the item
if ((Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance) &&
!_inDragMode)
{
var listView = sender as ListView;
//This is the problem the args OriginalSource is a scroll viewer here
var listViewItem =
VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject)args.OriginalSource);
if (listViewItem == null)
{
return;
}
m_AdornerLayer = InitializeAdornerLayer(listViewItem, listView);
UpdateDragAdornerLocation(args.GetTouchPoint(listView).Position);
mousePoint = mousePos;
_inDragMode = true;
DataObject dragData = new DataObject(listViewItem.Content as DNA2RunInfoDisplay);
listView.CaptureTouch(args.TouchDevice);
DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
_inDragMode = false;
IsScreenTouched = false;
// Reset this here... the mouse up event DOES NOT get raised if we do a drag/drop effect
StartPoint = null;
SelectedDragItem = null;
}
}
}
}
Here is the entire source code in case if it helps someone.
private void DoDragandDrop(ListViewItem item, DataObject obj, DragDropEffects effect)
{
if ((item != null) && (obj != null))
{
DragDrop.DoDragDrop(item, obj, effect);
}
}
private void GlobalHandler(object sender, InputEventArgs e)
{
Point? startPoint;
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewMouseLeftButtonDownEvent))
{
MouseButtonEventArgs args = e as MouseButtonEventArgs;
ListViewItem item = VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject) args.OriginalSource);
if (item != null)
{
DNA2RunInfoDisplay content = item.Content as DNA2RunInfoDisplay;
if ((content != null) && (content.PlateIndex == -2147483648))
{
return;
}
this.SelectedDragItem = item;
this.StartPoint = new Point?(args.GetPosition(null));
}
}
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewTouchDownEvent))
{
ListView listView = sender as ListView;
TouchEventArgs args2 = e as TouchEventArgs;
object selectedItem = this.RunSetupListView.SelectedItem;
ListViewItem itemToDrag = VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject) args2.OriginalSource);
if (itemToDrag != null)
{
itemToDrag.Focus();
this.SelectedDragItem = itemToDrag;
this.StartPoint = new Point?(args2.GetTouchPoint(null).Position);
this.m_AdornerLayer = this.InitializeAdornerLayer(itemToDrag, listView);
this.UpdateDragAdornerLocation(args2.GetTouchPoint(listView).Position);
if (this.SelectedDragItem.Content != BindingOperations.DisconnectedSource)
{
this.dataObject = new DataObject(this.SelectedDragItem.Content as DNA2RunInfoDisplay);
}
listView.CaptureTouch(args2.TouchDevice);
}
}
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewMouseLeftButtonUpEvent))
{
startPoint = null;
this.StartPoint = startPoint;
this.SelectedDragItem = null;
}
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewTouchUpEvent))
{
startPoint = null;
this.StartPoint = startPoint;
this.SelectedDragItem = null;
this.dataObject = null;
if (this.m_DragAdorner != null)
{
this.m_DragAdorner.Visibility = Visibility.Collapsed;
}
}
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewMouseMoveEvent))
{
MouseEventArgs args3 = e as MouseEventArgs;
if (!(sender is ListView))
{
return;
}
if ((args3.LeftButton == MouseButtonState.Pressed) && ((this.StartPoint != null) && (this.SelectedDragItem != null)))
{
Point position = args3.GetPosition(null);
startPoint = this.StartPoint;
Vector vector = startPoint.Value - position;
if (((Math.Abs(vector.get_X()) > SystemParameters.MinimumHorizontalDragDistance) || (Math.Abs(vector.get_Y()) > SystemParameters.MinimumVerticalDragDistance)) && !this._inDragMode)
{
ListView listView = sender as ListView;
ListViewItem selectedDragItem = this.SelectedDragItem;
if (selectedDragItem == null)
{
return;
}
this.m_AdornerLayer = this.InitializeAdornerLayer(selectedDragItem, listView);
this.UpdateDragAdornerLocation(args3.GetPosition(listView));
this.mousePoint = position;
this._inDragMode = true;
listView.CaptureMouse();
DragDrop.DoDragDrop(selectedDragItem, new DataObject(selectedDragItem.Content as DNA2RunInfoDisplay), DragDropEffects.Move);
listView.ReleaseMouseCapture();
this._inDragMode = false;
startPoint = null;
this.StartPoint = startPoint;
this.SelectedDragItem = null;
}
}
}
if (object.ReferenceEquals(e.RoutedEvent, UIElement.PreviewTouchMoveEvent))
{
Point position = (e as TouchEventArgs).GetTouchPoint(null).Position;
startPoint = this.StartPoint;
if ((startPoint == null) || (this.SelectedDragItem == null))
{
if (this.m_DragAdorner != null)
{
this.m_DragAdorner.Visibility = Visibility.Collapsed;
}
}
else
{
startPoint = this.StartPoint;
Point point3 = position;
if ((startPoint != null) ? ((startPoint != null) ? (startPoint.GetValueOrDefault() != point3) : false) : true)
{
startPoint = this.StartPoint;
Vector vector2 = startPoint.Value - position;
if (((Math.Abs(vector2.get_X()) > SystemParameters.MinimumHorizontalDragDistance) || (Math.Abs(vector2.get_Y()) > SystemParameters.MinimumVerticalDragDistance)) && (this.dataObject != null))
{
this.DoDragandDrop(this.SelectedDragItem, this.dataObject, DragDropEffects.Move);
}
}
}
this.SelectedDragItem = null;
startPoint = null;
this.StartPoint = startPoint;
}
}
private void HandleDragEvent(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(DNA2RunInfoDisplay)))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
else
{
DependencyObject originalSource = e.OriginalSource as DependencyObject;
if (originalSource == null)
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
else
{
ListViewItem item = VisualTreeHelperUtils.FindParent<ListViewItem>(originalSource);
if (item == null)
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
else
{
if ((item.Content as DNA2RunInfoDisplay).RunState == DNA2RunStates.Unassigned)
{
e.Effects = DragDropEffects.None;
}
else
{
ListView relativeTo = sender as ListView;
if (relativeTo != null)
{
Point point = item.TransformToAncestor((Visual) relativeTo).Transform(new Point(0.0, 0.0));
this.UpdateDragAdornerLocation(new Point(point.get_X(), e.GetPosition(relativeTo).get_Y()));
this.m_DragAdorner.Visibility = Visibility.Visible;
}
e.Effects = e.AllowedEffects;
}
e.Handled = true;
}
}
}
}
private AdornerLayer InitializeAdornerLayer(ListViewItem itemToDrag, ListView listView)
{
VisualBrush brush = new VisualBrush(itemToDrag);
this.m_DragAdorner = new BRDragAdornerWithVisualBrush(listView, itemToDrag.RenderSize, brush);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(listView);
adornerLayer.Add(this.m_DragAdorner);
return adornerLayer;
}
private void RunSetupListView_DragEnter(object sender, DragEventArgs e)
{
this.HandleDragEvent(sender, e);
}
private void RunSetupListView_DragLeave(object sender, DragEventArgs e)
{
if (this.m_DragAdorner != null)
{
this.m_DragAdorner.Visibility = Visibility.Collapsed;
}
}
private void RunSetupListView_DragOver(object sender, DragEventArgs e)
{
FrameworkElement depObj = sender as FrameworkElement;
if (depObj != null)
{
ScrollViewer firstVisualChild = VisualTreeHelperUtils.GetFirstVisualChild<ScrollViewer>(depObj);
if (firstVisualChild != null)
{
double num = e.GetPosition(depObj).get_Y();
if (num < 30.0)
{
firstVisualChild.ScrollToVerticalOffset(firstVisualChild.VerticalOffset - 20.0);
}
else if (num > (depObj.ActualHeight - 30.0))
{
firstVisualChild.ScrollToVerticalOffset(firstVisualChild.VerticalOffset + 20.0);
}
this.HandleDragEvent(sender, e);
}
}
}
private void RunSetupListView_Drop(object sender, DragEventArgs e)
{
try
{
this._inDragMode = false;
if (e.Data.GetDataPresent(typeof(DNA2RunInfoDisplay)))
{
DNA2RunInfoDisplay data = (DNA2RunInfoDisplay) e.Data.GetData(typeof(DNA2RunInfoDisplay));
ListViewItem item = VisualTreeHelperUtils.FindParent<ListViewItem>((DependencyObject) e.OriginalSource);
if (item != null)
{
this.DNA2RunSetupVM.ChangeRunPriority(data, item.Content as DNA2RunInfoDisplay);
this.PropertyChanged(this, new PropertyChangedEventArgs("RunSetupInfoList"));
}
}
}
finally
{
if (this.m_DragAdorner != null)
{
this.m_DragAdorner.Visibility = Visibility.Collapsed;
}
}
}
I'm creating a memory game for my schoolproject and i have a problem of triggering a messagebox when player won the game and displays a note "You hav matched all cards!". Does anyone have the solution.
One part of the code:
private void Card1_Click(object sender, EventArgs e)
{
Card1.Image = Properties.Resources.car1;
if(usedCard1 == null)
{
usedCard1 = Card1;
}
else if(usedCard1 != null && usedCard2 == null)
{
usedCard2 = Card1;
}
if(usedCard1 != null && usedCard2 != null)
{
if(usedCard1.Tag == usedCard2.Tag)
{
usedCard1 = null;
usedCard2 = null;
Card1.Enabled = false;
Dupcard1.Enabled = false;
points = Convert.ToInt32(ScoreCounter.Text);
points = points + 10;
ScoreCounter.Text = Convert.ToString(points);
}
else
{
points = Convert.ToInt32(ScoreCounter.Text);
points = points - 10;
ScoreCounter.Text = Convert.ToString(points);
timer4.Start();
}
}
}
private void Dupcard1_Click(object sender, EventArgs e)
{
Dupcard1.Image = Properties.Resources.car1;
if (usedCard1 == null)
{
usedCard1 = Dupcard1;
}
else if (usedCard1 != null && usedCard2 == null)
{
usedCard2 = Dupcard1;
}
if (usedCard1 != null && usedCard2 != null)
{
if (usedCard1.Tag == usedCard2.Tag)
{
usedCard1 = null;
usedCard2 = null;
Card1.Enabled = false;
Dupcard1.Enabled = false;
points = Convert.ToInt32(ScoreCounter.Text);
points = points + 10;
ScoreCounter.Text = Convert.ToString(points);
}
else
{
points = Convert.ToInt32(ScoreCounter.Text);
points = points - 10;
ScoreCounter.Text = Convert.ToString(points);
timer4.Start();
}
}
}
private void Win()
{
foreach(PictureBox picture in cardsHolder.Controls)
{
if(picture != null)
{
}
}
MessageBox.Show("You've matched all cards", "Congratulations");
Close();
}
PS: cardsHolder is the name of the panel, and Win is the method of triggering message when player wins the game.
I presume that you set the PictureBox.Image property to null if you don't show an image on a PictureBox. So simply check that property:
private void Win()
{
if (cardsHolder.Controls.OfType<PictureBox>().Any(pb => pb.Image == null))
return;
MessageBox.Show("You matched all the icons!", "Congratulations");
Close();
}
OfType<PictureBox> selects all PictureBoxes from the Controls collection and Any returns true if one of them has no Image.
I have a custom Datagrid, in that datagrid I change the way we select element.
I have some feature added to the selection, like :
When we select element, it's like the "Ctrl" key was press.
When we click on a selected row, the row become unselected.
When we do a multiple selection, all the row change the selectedValue for the one that the first row is going to have.
When we do a multiple selection (mouse down, move, then mouse up) with Right click it's reversing the selected value of the rows.
It's a DataGrid extension, so I am coding only in C#.
For doing that I added event handle on PreviewMouseDown and MouseUp of for the datagridrow.
private enum ButtonClicked {Left, Middle, Right};
private ButtonClicked m_oMouseButtonClicked;
private void PreviewMouseDownHandler(object sender, MouseButtonEventArgs e)
{
DataGridRow row = sender as DataGridRow;
if (e.LeftButton == MouseButtonState.Pressed)
{
row.IsSelected = !row.IsSelected;
m_oMouseButtonClicked = ButtonClicked.Left;
}
else if (e.RightButton == MouseButtonState.Pressed)
{
//row.IsSelected = !row.IsSelected;
m_oMouseButtonClicked = ButtonClicked.Right;
}
row.CaptureMouse();
row.MouseMove += row_MouseMove;
e.Handled = true;
}
void row_MouseMove(object sender, MouseEventArgs e)
{
Point oPosFromThis = e.GetPosition(this);
if (oPosFromThis.Y > this.ActualHeight)
{
}
else if (oPosFromThis.Y < 0)
{
}
}
void Row_MouseUp(object sender, MouseButtonEventArgs e)
{
int nStart;
int nEnd;
DataGridRow row = sender as DataGridRow;
row.ReleaseMouseCapture();
row.MouseMove -= row_MouseMove;
int nStartRowIndex = ItemContainerGenerator.IndexFromContainer(row);
Point oPosFromRow = e.MouseDevice.GetPosition(row);
int nEndRowIndex = nStartRowIndex + (int)Math.Floor(oPosFromRow.Y / row.ActualHeight);
if (nStartRowIndex < nEndRowIndex)
{
nStart = Math.Max(nStartRowIndex, 0);
nEnd = Math.Min(nEndRowIndex, Items.Count - 1);
}
else
{
nStart = Math.Max(nEndRowIndex, 0);
nEnd = Math.Min(nStartRowIndex, Items.Count - 1);
}
for (; nStart <= nEnd; ++nStart)
{
DataGridRow oTmp = ((DataGridRow)ItemContainerGenerator.ContainerFromIndex(nStart));
if (m_oMouseButtonClicked == ButtonClicked.Left)
{
oTmp.IsSelected = row.IsSelected;
}
else if (m_oMouseButtonClicked == ButtonClicked.Right)
{
oTmp.IsSelected = !oTmp.IsSelected;
}
}
e.Handled = true;
}
I give the mouse capture to my row i clicked, to be able to catch the mouseUp even if i go outside the datagrid.
But with my code, I lost a feature that i would like to have. The auto scrolling when I do a multiple selection and i go under or upper the datagrid. I know that iI will have to add MouseMove Handler to do it, but for now i am stuck cause I don't know how to do it.
I finally found a solution by try-error attempts. I added method to get the scrollviewer element, then i am starting a Timer to execute the scroll alone.
public claa AAA
{
private enum ButtonClicked {Left, Middle, Right};
private ButtonClicked m_oMouseButtonClicked;
private DispatcherTimer m_oTimer;
private double m_nScrollOffset;
private ScrollViewer m_oScrollBar;
public IcuAlertGrid()
{
this.Initialized += IcuAlertGrid_Initialized;
this.Loaded += IcuAlertGrid_Loaded;
m_oTimer = new DispatcherTimer();
m_oTimer.Tick += m_oTimer_Tick;
m_oTimer.Interval = new TimeSpan(2500000);
}
void IcuAlertGrid_Initialized(object sender, EventArgs e)
{
setStyle0(true);
//throw new NotImplementedException();
}
void IcuAlertGrid_Loaded(object sender, RoutedEventArgs e)
{
m_oScrollBar = GetScrollViewer(this);
}
void m_oTimer_Tick(object sender, EventArgs e)
{
if (m_oScrollBar != null)
{
m_oScrollBar.ScrollToVerticalOffset(m_oScrollBar.VerticalOffset + m_nScrollOffset);
}
}
private void PreviewMouseDownHandler(object sender, MouseButtonEventArgs e)
{
DataGridRow row = sender as DataGridRow;
if (e.LeftButton == MouseButtonState.Pressed)
{
row.IsSelected = !row.IsSelected;
m_oMouseButtonClicked = ButtonClicked.Left;
}
else if (e.RightButton == MouseButtonState.Pressed)
{
//row.IsSelected = !row.IsSelected;
m_oMouseButtonClicked = ButtonClicked.Right;
}
row.CaptureMouse();
row.MouseMove += row_MouseMove;
e.Handled = true;
}
private void row_MouseMove(object sender, MouseEventArgs e)
{
DataGridRow oRow = sender as DataGridRow;
Point oPosFromThis = e.GetPosition(this);
if (oPosFromThis.Y < 0)
{
m_nScrollOffset = -1.0;
m_oTimer.Start();
}
else if (this.ActualHeight < oPosFromThis.Y)
{
m_nScrollOffset = 1.0;
m_oTimer.Start();
}
else
{
m_oTimer.Stop();
}
}
private void Row_MouseUp(object sender, MouseButtonEventArgs e)
{
int nStart;
int nEnd;
m_oTimer.Stop();
DataGridRow row = sender as DataGridRow;
row.ReleaseMouseCapture();
row.MouseMove -= row_MouseMove;
int nStartRowIndex = ItemContainerGenerator.IndexFromContainer(row);
Point oPosFromRow = e.MouseDevice.GetPosition(row);
int nEndRowIndex = nStartRowIndex + (int)Math.Floor(oPosFromRow.Y / row.ActualHeight);
if (nStartRowIndex < nEndRowIndex)
{
nStart = Math.Max(nStartRowIndex, 0);
nEnd = Math.Min(nEndRowIndex, Items.Count - 1);
}
else
{
nStart = Math.Max(nEndRowIndex, 0);
nEnd = Math.Min(nStartRowIndex, Items.Count - 1);
}
for (; nStart <= nEnd; ++nStart)
{
DataGridRow oTmp = ((DataGridRow)ItemContainerGenerator.ContainerFromIndex(nStart));
if (m_oMouseButtonClicked == ButtonClicked.Left)
{
oTmp.IsSelected = row.IsSelected;
}
else if (m_oMouseButtonClicked == ButtonClicked.Right)
{
oTmp.IsSelected = !oTmp.IsSelected;
}
}
e.Handled = true;
}
private static ScrollViewer GetScrollViewer(DependencyObject p_oParent)
{
ScrollViewer child = default(ScrollViewer);
int numVisuals = VisualTreeHelper.GetChildrenCount(p_oParent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(p_oParent, i);
child = v as ScrollViewer;
if (child == null)
{
child = GetScrollViewer(v);
}
if (child != null)
{
break;
}
}
return child;
}
}