I need to have fixed on the right on the window a textblock where I set Note, the rest of the window could be a listbox filled of selectable items. My problem is that Note can be visible or not and the selectable items should be wrapped all around like in Word with image and Wrap Text set to "Square"
Examples:
ADDED
I created a custom UniformGrid, I added a property NoteRowToSkip that rapresent the rows to skip in the last column on the left. I override ArrangeOverride and added the the skip behavior that I want.
Debugging seems that values are correct, but has the same behavior of UniformGrid.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= childBounds.Height;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return true;
return column != Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}
Any idea in what's not working in my code?
SOLVED
Was a problem with if conditions.
The working version is below.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= xStep;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return false;
return column == Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}
WrapPanel with a bit of tuning could help you to achieve something like this:
Here I used ItemsControl as a host for items with WrapPanel as ItemsPanelTemplate:
<ItemsControl x:Name="panel">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:NoteItem}">
<TextBlock HorizontalAlignment="Right" TextWrapping="Wrap" Width="50" Height="150" Margin="10"
Text="{Binding Text}" Background="LightBlue">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Visible}" Value="False" >
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ListItem}">
<TextBlock TextWrapping="Wrap" Width="50" Height="50" Margin="10" Text="{Binding Text}" Background="LightGray"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And to try it put that in code behind:
public class Item
{
public string Text { get; set; }
}
public class NoteItem:Item
{
public bool Visible{ get; set; }
}
public class ListItem : Item
{
}
public partial class MainWindow : Window
{
ObservableCollection<Item> list = new ObservableCollection<Item>();
public MainWindow()
{
InitializeComponent();
NoteItem note = new NoteItem { Text = "This is a note", Visible = true };
list.Add(note);
for(int i=0;i<20; i++)
list.Add(new ListItem { Text = "This is a list item" });
panel.ItemsSource = list;
}
}
Related
I've a listview that is dynamically populated by code.
In some case I need to put a note that has different height respect other components.
My goal is to have Note and listview items wrap all around, like in Word with image and Wrap Text set to "Square"
I tried to use UniformGrid and WrapPanel.
I tried to understand how write my own WrapPanel, but I don't find a way to solve it.
I think that I can have an external component for Note and leave listview items that are under Note like "Hidden"
<ListView x:Name="PluginListView"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
SelectionMode="Single"
VerticalContentAlignment="Top"
BorderThickness="0"
Margin="0,10,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--<UniformGrid Columns="4" HorizontalAlignment="Stretch" Rows="10"/>-->
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
ADDED
I created a custom UniformGrid, I added a property NoteRowToSkip that rapresent the rows to skip in the last column on the left. I override ArrangeOverride and added the the skip behavior that I want.
Debugging seems that values are correct, but has the same behavior of UniformGrid.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= childBounds.Height;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return true;
return column != Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}
Any idea in what's not working in my code?
SOLVED
Was a problem with if conditions.
The working version is below.
public class UniformNoteWrapGrid : Panel
{
private int m_Rows;
private int m_Columns;
public static readonly DependencyProperty NoteRowToSkipProperty = DependencyProperty.Register(nameof(NoteRowToSkip), typeof(int), typeof(UniformGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateNoteRowToSkip);
private static bool ValidateNoteRowToSkip(object o)
{
return (int)o >= 0;
}
public int Columns
{
get => (int)GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns",
typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateColumns);
private static bool ValidateColumns(object o)
{
return (int)o >= 0;
}
public int Rows
{
get => (int)GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty RowsProperty =
DependencyProperty.Register("Rows", typeof(int),
typeof(UniformGrid),
new FrameworkPropertyMetadata(
0,
FrameworkPropertyMetadataOptions.AffectsMeasure),
ValidateRows);
private static bool ValidateRows(object o)
{
return (int)o >= 0;
}
public int NoteRowToSkip
{
get => (int)GetValue(NoteRowToSkipProperty);
set => SetValue(NoteRowToSkipProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
UpdateComputedValues();
var childConstraint = new Size(constraint.Width / m_Columns, constraint.Height / m_Rows);
double maxChildDesiredWidth = 0.0;
double maxChildDesiredHeight = 0.0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
child.Measure(childConstraint);
var childDesiredSize = child.DesiredSize;
if (maxChildDesiredWidth < childDesiredSize.Width)
maxChildDesiredWidth = childDesiredSize.Width;
if (maxChildDesiredHeight < childDesiredSize.Height)
maxChildDesiredHeight = childDesiredSize.Height;
}
return new Size(maxChildDesiredWidth * m_Columns, maxChildDesiredHeight * m_Rows);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var childBounds = new Rect(0, 0, arrangeSize.Width / m_Columns, arrangeSize.Height / m_Rows);
double xStep = childBounds.Width;
double xBound = arrangeSize.Width - 1.0;
var row = 1;
var column = 1;
foreach (UIElement child in InternalChildren)
{
child.Arrange(childBounds);
if (child.Visibility == Visibility.Collapsed)
continue;
childBounds.X += xStep;
column++;
var testXBound = xBound;
if (IsCellForNote(row, column))
testXBound -= xStep;
if (!(childBounds.X >= testXBound))
continue;
childBounds.Y += childBounds.Height;
childBounds.X = 0;
row++;
column = 1;
}
return arrangeSize;
}
private bool IsCellForNote(int row, int column)
{
if (row > NoteRowToSkip)
return false;
return column == Columns;
}
private void UpdateComputedValues()
{
m_Columns = Columns;
m_Rows = Rows;
//if (FirstColumn >= m_Columns)
// FirstColumn = 0;
if (m_Rows != 0 && m_Columns != 0)
return;
int nonCollapsedCount = 0;
for (int i = 0, count = InternalChildren.Count; i < count; ++i)
{
var child = InternalChildren[i];
if (child.Visibility != Visibility.Collapsed)
nonCollapsedCount++;
}
if (nonCollapsedCount == 0)
nonCollapsedCount = 1;
if (m_Rows == 0)
{
if (m_Columns > 0)
m_Rows = (nonCollapsedCount + (m_Columns - 1)) / m_Columns;
else
{
m_Rows = (int)Math.Sqrt(nonCollapsedCount);
if ((m_Rows * m_Rows) < nonCollapsedCount)
m_Rows++;
m_Columns = m_Rows;
}
}
else if (m_Columns == 0)
m_Columns = (nonCollapsedCount + (m_Rows - 1)) / m_Rows;
}
}
Hey there,
the generation of my Point3DCollection works fine and i get the Mesh in the Viewport3D (https://imgur.com/5Hzitw2), but the calculateNoise() doesnt update my bound Positions in the Viewport3D even though the OnPropertyChanged Method in my ViewModel get's called. What am I doing wrong here?
Thanks in advance,
KonstIT
XAML:
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera x:Name="camera"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<DirectionalLight Color="White" Direction="-1, -1, -3" />
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="{Binding Positions}" TriangleIndices="{Binding TriangleIndices}"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="White"/>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
ViewportViewModel:
internal class ViewportViewModel : INotifyPropertyChanged
{
private int _terrainSize;
private Point3DCollection _positions;
private Int32Collection _triangleIndices;
private Random random = new Random();
public Point3DCollection Positions
{
get
{
return _positions;
}
set
{
_positions = value;
OnPropertyChanged("Positions");
}
}
public Int32Collection TriangleIndices
{
get
{
return _triangleIndices;
}
set
{
_triangleIndices = value;
OnPropertyChanged("TriangleIndices");
}
}
public ViewportViewModel()
{
_terrainSize = 56;
_positions = new Point3DCollection();
_triangleIndices = new Int32Collection();
GeneratePositions();
GenerateTriangleIndices();
CalculateNoiseCommand = new CalculateNoiseCommand(this);
}
private void GeneratePositions()
{ //Works }
private void GenerateTriangleIndices()
{ //Works }
//Handling
...
internal void CalculateNoise()
{
for (int i = 0; i < _terrainSize * _terrainSize; i++)
{
Point3D point = new Point3D();
point = Positions[i];
point.Y = random.Next(10) / 10.0;
Positions[i] = point;
}
OnPropertyChanged("Positions");
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
Console.WriteLine("ViewportViewModel: " + propertyName);
}
}
#endregion
}
The Code to replace the current Point3DCollection. It's the same as the GeneratePositions() which is called at the constructor, just with the Positions.Clear() method at the beginning. It's called via the CalculateNoise().
private void ReplacePositions()
{
if(Positions.Count > 0)
{
Positions.Clear();
}
var column = 0;
var row = 0;
Point3D point = new Point3D();
for (int i = 0; i < _terrainSize * _terrainSize; i++)
{
for (int j = 0; j < 3; j++)
{
if (j == 0)
point.X = ((float)column / ((float)_terrainSize - 1) - 0.5) * 2;
if (j == 1)
point.Y = 0;
if (j == 2)
point.Z = ((float)row / ((float)_terrainSize - 1) - 0.5) * 2;
}
Positions.Add(point);
// Calculate next row & column
column++;
if (column % _terrainSize == 0)
{
row++;
}
column %= _terrainSize;
}
}
internal void CalculateNoise()
{
ReplacePositions();
OnPropertyChanged("Positions");
}
public class GeometryTips : ViewModelBase
{
#region " Private Fields "
private int _terrainSize = 56;
private Random random = new Random();
#endregion
#region " Public Properties "
#region " MeshGeo "
private MeshGeometry3D _MeshGeo;
public MeshGeometry3D MeshGeo
{
get { return _MeshGeo; }
set { this.Set(ref _MeshGeo, value); }
}
#endregion
#endregion
#region " Commands "
#region " Create3DCommand "
public RelayCommand Create3DCommand { get; private set; }
private void Create3D()
{
for (int i = 0; i < _terrainSize * _terrainSize; i++)
{
Point3D point = new Point3D();
point = MeshGeo.Positions[i];
point.Y = random.Next(10) / 10.0;
MeshGeo.Positions[i] = point;
}
}
#endregion
#endregion
#region " Constructor "
/// <summary>
/// GeometryTips
/// </summary>
public GeometryTips()
{
this.Create3DCommand = new RelayCommand(Create3D);
this._MeshGeo = new MeshGeometry3D();
GeneratePositions();
GenerateTriangleIndices();
}
#endregion
#region " Private Functions "
#region " GeneratePositions "
/// <summary>
/// GeneratePositions
/// </summary>
private void GeneratePositions()
{
var column = 0;
var row = 0;
Point3D point = new Point3D();
for (int i = 0; i < _terrainSize * _terrainSize; i++)
{
for (int j = 0; j < 3; j++)
{
if (j == 0)
point.X = ((float)column / ((float)_terrainSize - 1) - 0.5) * 2;
if (j == 1)
point.Y = 0;
if (j == 2)
point.Z = ((float)row / ((float)_terrainSize - 1) - 0.5) * 2;
}
_MeshGeo.Positions.Add(point);
// Calculate next row & column
column++;
if (column % _terrainSize == 0)
{
row++;
}
column %= _terrainSize;
}
}
#endregion
#region " GenerateTriangleIndices "
/// <summary>
/// GenerateTriangleIndices
/// </summary>
private void GenerateTriangleIndices()
{
var value = 0;
for (int i = 0; i < _terrainSize * _terrainSize - _terrainSize; i++)
{
for (int triangle = 0; triangle < 3; triangle++)
{
if (i % _terrainSize == 0)
{
break;
}
if (triangle == 0)
{
value = i;
}
else if (triangle == 1)
{
value = i + triangle + _terrainSize - 2;
}
else if (triangle == 2)
{
value = i + triangle + _terrainSize - 2;
}
_MeshGeo.TriangleIndices.Add(value);
}
for (int triangle = 0; triangle < 3; triangle++)
{
if (i > 0 && ((i + 1) % _terrainSize) == 0)
{
break;
}
if (triangle == 0)
{
value = i + triangle;
}
else if (triangle == 1)
{
value = i + triangle + _terrainSize - 1;
}
else if (triangle == 2)
{
value = i + triangle - 1;
}
_MeshGeo.TriangleIndices.Add(value);
}
}
}
#endregion
#endregion
}
I created this viewmodel with MVVM Light.
I use a MeshGeometry3D class to bind against.
Here is my xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="10"></RowDefinition>
</Grid.RowDefinitions>
<Viewport3D Grid.Column="1" Grid.Row="1">
<Viewport3D.Camera>
<PerspectiveCamera x:Name="camera"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<DirectionalLight Direction="-1, -1, -3"/>
<GeometryModel3D Geometry="{Binding MeshGeo}">
<GeometryModel3D.Material>
<DiffuseMaterial Brush="White"/>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Button Grid.Column="1" Grid.Row="2" Content="Handle" Command="{Binding Create3DCommand}"></Button>
</Grid>
This was working for me, hope it helps...
I have two custom controls for displaying collections of comments and signatures. Each of these controls has a button for changing display style - show entire collection or just first 4 elements.
When I put these controls in a default WrapPanel - they work fine: expand or collapse depending on user's actions.
In my own custom WrapPanel with new ArrangeOverride function they also work fine.
Now I need WrapPanel with option "LastChildFill" (I found example here) and with custom "WrapDirection" (similar to FlowDirection). First or last element in WrapPanel have to fill all remaining space while it's more than MinWidth of the element.
So I've created third WrapPanel with custom MeasureOverride and ArrangeOverride. And it works as I want, but when I change control display style a collection inside a control changes, but MeasureOverride in my WrapPanel doesn't start until I change window size.
Update: MeasureOverride doesn't start for expanding first or last element which fills remaining space. Collapsing for this element works fine.
I don't understand what thing I have broken with custom MeasureOverride?
Update 2:
I fugured that my problem is similar to this: MeasureOverride not always called on children's property changes.
And attach MeasureOverride code.
public class WrapPanelFlowDirection : WrapPanel
{
public bool LastChildFill
{
get { return (bool)GetValue(LastChildFillProperty); }
set { SetValue(LastChildFillProperty, value); }
}
public static readonly DependencyProperty LastChildFillProperty =
DependencyProperty.Register("LastChildFill", typeof(bool), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure));
public WrapDirection WrapDirection
{
get { return (WrapDirection)GetValue(WrapDirectionProperty); }
set { SetValue(WrapDirectionProperty, value); }
}
public static readonly DependencyProperty WrapDirectionProperty =
DependencyProperty.Register("WrapDirection", typeof(WrapDirection), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(WrapDirection.LeftToRight, FrameworkPropertyMetadataOptions.AffectsMeasure));
#region Measure Override
protected override Size MeasureOverride(Size availableSize)
{
var panelSize = new Size(availableSize.Width, 0);
double minWidth = 0;
foreach (FrameworkElement child in this.InternalChildren)
{
child.Measure(availableSize);
minWidth += child.DesiredSize.Width;
}
int firstChildInLine = 0;
double currLineHeight = 0;
double currLineWidth = 0;
UIElementCollection children = this.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (currLineWidth + w <= availableSize.Width)
{
currLineWidth += w;
currLineHeight = Math.Max(currLineHeight, child.DesiredSize.Height);
}
else
{
MeasureOneLine(firstChildInLine, i - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineWidth = w;
currLineHeight = child.DesiredSize.Height;
if (w > availableSize.Width)
{
MeasureOneLine(i, i, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineHeight = currLineWidth = 0;
firstChildInLine = i + 1;
}
else
{
firstChildInLine = i;
}
}
}
if (firstChildInLine < children.Count)
{
MeasureOneLine(firstChildInLine, children.Count - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
}
if (double.IsInfinity(panelSize.Width))
panelSize.Width = minWidth;
return panelSize;
}
private void MeasureOneLine(int start, int end, ref double height, double totalWidth)
{
UIElementCollection children = this.InternalChildren;
if (WrapDirection == WrapDirection.LeftToRight)
{
for (int i = start; i < end; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i < end)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
else
{
for (int i = end; i >= start; i--)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i > start)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
}
#endregion
I found solution here : How does a container know when a child has called InvalidateArrange?
And it worked for me.
public static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
if(panel != null)
{
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
}
I am trying to create a Paginator that will in turn create an XpsDocument that I can preview with the and then print. I have the following code that I found on the web but do not understand how it is working.
The issue I am having is that if I run this as is, the pages are generated successfully. If I comment out the lines within OnRender() that generate the actual values of data (all the lines after Random...) I get pages that are about one row high and no text on them but they appear to be the correct length. What is it that is keeping the values of "Row Number" & "Column i" from being shown?
I have included 2 screen shots to illustrate.
public class TaxCodePrintPaginator : DocumentPaginator
{
private int _RowsPerPage;
private Size _PageSize;
private int _Rows;
private List<TaxCode> _dataList;
public TaxCodePrintPaginator(List<TaxCode> dt, int rows, Size pageSize)
{
_dataList = dt;
_Rows = rows;
PageSize = pageSize;
}
public override DocumentPage GetPage(int pageNumber)
{
int currentRow = _RowsPerPage * pageNumber;
var page = new PageElement(currentRow, Math.Min(_RowsPerPage, _Rows - currentRow))
{
Width = PageSize.Width,
Height = PageSize.Height
};
page.Arrange(new Rect(new Point(0, 0), PageSize));
return new DocumentPage(page);
}
public override bool IsPageCountValid
{ get { return true; } }
public override int PageCount
{ get { return (int)Math.Ceiling(_Rows / (double)_RowsPerPage); } }
public override Size PageSize
{
get { return _PageSize; }
set
{
_PageSize = value;
_RowsPerPage = 40;
//Can't print anything if you can't fit a row on a page
Debug.Assert(_RowsPerPage > 0);
}
}
public override IDocumentPaginatorSource Source
{ get { return null; } }
}
public class PageElement : UserControl
{
private const int PageMargin = 75;
private const int HeaderHeight = 25;
private const int LineHeight = 20;
private const int ColumnWidth = 140;
private int _CurrentRow;
private int _Rows;
public PageElement(int currentRow, int rows)
{
Margin = new Thickness(PageMargin);
_CurrentRow = currentRow;
_Rows = rows;
}
private static FormattedText MakeText(string text)
{
return new FormattedText(text, CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Tahoma"), 14, Brushes.Black);
}
public static int RowsPerPage(double height)
{
return (int)Math.Floor((height - (2 * PageMargin)
- HeaderHeight) / LineHeight);
}
protected override void OnRender(DrawingContext dc)
{
Point curPoint = new Point(0, 0);
dc.DrawText(MakeText("Row Number"), curPoint);
curPoint.X += ColumnWidth;
for (int i = 1; i < 4; i++)
{
dc.DrawText(MakeText("Column " + i), curPoint);
curPoint.X += ColumnWidth;
}
curPoint.X = 0;
curPoint.Y += LineHeight;
dc.DrawRectangle(Brushes.Black, null, new Rect(curPoint, new Size(Width, 2)));
curPoint.Y += HeaderHeight - LineHeight;
Random numberGen = new Random();
for (int i = _CurrentRow; i < _CurrentRow + _Rows; i++)
{
dc.DrawText(MakeText(i.ToString()), curPoint);
curPoint.X += ColumnWidth;
for (int j = 1; j < 4; j++)
{
dc.DrawText(MakeText(numberGen.Next().ToString()), curPoint);
curPoint.X += ColumnWidth;
}
curPoint.Y += LineHeight;
curPoint.X = 0;
}
}
}
Before
After
I want to create a ListBox that basically displays a circular list. For example a list with three items:
1
2
3 <- (Selector on item 3)
will rotate the list and have the selector over item 1 after passing item 3:
2
3
1 <- (Selector on item 1)
This is similar to the menu paradigm of the Windows Media Center.
What part of the ListBox should I be tweaking???
Well, I updated the Items... not sure it's the right way, but, anyhow, if you want to take a look at my implementation... here it is:
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using ScrollLimit;
using System.Windows.Controls.Primitives;
using System.Text.RegularExpressions;
namespace ScrollLimit
{
public enum ScrollLimitBehaviour { Springs, Circular };
}
namespace SmoothScroll
{
[TemplatePart(Name = "PART_Border", Type = typeof(Border))]
[TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))]
[TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))]
public class SmoothScrollViewer : ListBox
{
private double move_init, scroll_init, scroll_offset, scroll_t0, scroll_span, scroll_v0, scroll_offset0, backOffset, adjustLimit, stopWindowCoord, hitOffset;
private bool scroll_mouseDown = false, scroll_direction = false, scroll_out = false, scrollLessThanZero = false, scrollMoreThanEnd = false, selectionAllowed = false, canUpdate = true, external = true;
private DispatcherTimer scroll_timerClock = new DispatcherTimer();
private EventHandler decelerateEventHandler, adjustEventHandler;
private int selectedIndex;
private ScrollViewer sv;
private Rectangle stopWindow;
private StackPanel sp;
private ListBoxItem lbi;
private Grid g;
private TextBox filterEdit;
//proprietatea de orientare a listei
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation",
typeof(Orientation),
typeof(SmoothScrollViewer),
new UIPropertyMetadata(Orientation.Vertical,
new PropertyChangedCallback(OnOrientationChanged),
new CoerceValueCallback(OnCoerceOrientation))
);
private static object OnCoerceOrientation(DependencyObject o, Object value)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) return ssw.OnCoerceOrientation((Orientation)value);
else return value;
}
private static void OnOrientationChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) ssw.OnOrientationChanged((Orientation)e.OldValue, (Orientation)e.NewValue);
}
protected virtual Orientation OnCoerceOrientation(Orientation value)
{
return value;
}
protected virtual void OnOrientationChanged(Orientation oldValue, Orientation newValue)
{
}
public Orientation Orientation
{
get
{
return (Orientation)GetValue(OrientationProperty);
}
set
{
SetValue(OrientationProperty, value);
}
}
//proprietatea de multiplicator de deplasament a chenarului de oprire a elementului evidentiat
public static readonly DependencyProperty StopWindowOffsetProperty =
DependencyProperty.Register("StopWindowOffset",
typeof(double),
typeof(SmoothScrollViewer),
new UIPropertyMetadata(-1.0,
new PropertyChangedCallback(OnStopWindowOffsetChanged),
new CoerceValueCallback(OnCoerceStopWindowOffset))
);
private static object OnCoerceStopWindowOffset(DependencyObject o, Object value)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) return ssw.OnCoerceStopWindowOffset((double)value);
else return value;
}
private static void OnStopWindowOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) ssw.OnStopWindowOffsetChanged((double)e.OldValue, (double)e.NewValue);
}
protected virtual double OnCoerceStopWindowOffset(double value)
{
return value;
}
protected virtual void OnStopWindowOffsetChanged(double oldValue, double newValue)
{
}
public double StopWindowOffset
{
get
{
return (double)GetValue(StopWindowOffsetProperty);
}
set
{
SetValue(StopWindowOffsetProperty, value);
}
}
//proprietatea de comportament la capete
public static readonly DependencyProperty ScrollLimitBehaviourProperty =
DependencyProperty.Register("ScrollLimitBehaviour",
typeof(ScrollLimitBehaviour),
typeof(SmoothScrollViewer),
new UIPropertyMetadata(ScrollLimitBehaviour.Springs,
new PropertyChangedCallback(OnScrollLimitBehaviourChanged),
new CoerceValueCallback(OnCoerceScrollLimitBehaviour))
);
private static object OnCoerceScrollLimitBehaviour(DependencyObject o, Object value)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) return ssw.OnCoerceScrollLimitBehaviour((ScrollLimitBehaviour)value);
else return value;
}
private static void OnScrollLimitBehaviourChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) ssw.OnScrollLimitBehaviourChanged((ScrollLimitBehaviour)e.OldValue, (ScrollLimitBehaviour)e.NewValue);
}
protected virtual ScrollLimitBehaviour OnCoerceScrollLimitBehaviour(ScrollLimitBehaviour value)
{
return value;
}
protected virtual void OnScrollLimitBehaviourChanged(ScrollLimitBehaviour oldValue, ScrollLimitBehaviour newValue)
{
}
public ScrollLimitBehaviour ScrollLimitBehaviour
{
get
{
return (ScrollLimitBehaviour)GetValue(ScrollLimitBehaviourProperty);
}
set
{
SetValue(ScrollLimitBehaviourProperty, value);
}
}
//proprietatea de existenta a filtrarii
public static readonly DependencyProperty CanFilterProperty =
DependencyProperty.Register("CanFilter",
typeof(bool),
typeof(SmoothScrollViewer),
new UIPropertyMetadata(false,
new PropertyChangedCallback(OnCanFilterChanged),
new CoerceValueCallback(OnCoerceCanFilter))
);
private static object OnCoerceCanFilter(DependencyObject o, Object value)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) return ssw.OnCoerceCanFilter((bool)value);
else return value;
}
private static void OnCanFilterChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
SmoothScrollViewer ssw = o as SmoothScrollViewer;
if (ssw != null) ssw.OnCanFilterChanged((bool)e.OldValue, (bool)e.NewValue);
}
protected virtual bool OnCoerceCanFilter(bool value)
{
return value;
}
protected virtual void OnCanFilterChanged(bool oldValue, bool newValue)
{
}
public bool CanFilter
{
get
{
return (bool)GetValue(CanFilterProperty);
}
set
{
SetValue(CanFilterProperty, value);
}
}
//previne scroll-ul prin drag in afara listei
protected override void OnMouseMove(MouseEventArgs e)
{
}
protected override void OnIsMouseCapturedChanged(DependencyPropertyChangedEventArgs e)
{
}
//copie ItemsSource in Items
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
if (ItemsSource != null)
{
ObservableCollection<object> temp = new ObservableCollection<object>();
foreach (object o in ItemsSource) temp.Add(o);
ItemsSource = null;
for (int i = 0; i < temp.Count; i++) Items.Add(temp[i]);
}
}
//returneaza elementul primit ca parametru ca ListBoxItem
private ListBoxItem getListBoxItem(UIElement element)
{
if (element is ListBoxItem) return element as ListBoxItem;
while (element != this && element != null)
{
element = VisualTreeHelper.GetParent(element) as UIElement;
if (element is ListBoxItem) return element as ListBoxItem;
}
return null;
}
//la aplicarea sablonului vizual au loc asocierile de evenimente si initializarile
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
sv = GetTemplateChild("PART_ScrollViewer") as ScrollViewer;
sp = GetTemplateChild("PART_StackPanel") as StackPanel;
g = GetTemplateChild("PART_Grid") as Grid;
if ((ScrollBarVisibility)GetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty) == ScrollBarVisibility.Disabled) sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
if ((ScrollBarVisibility)GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty) == ScrollBarVisibility.Disabled) sv.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
sp.PreviewMouseDown += new MouseButtonEventHandler(l_MouseDown);
sp.PreviewMouseMove += new MouseEventHandler(l_MouseMove);
sp.PreviewMouseUp += new MouseButtonEventHandler(l_MouseUp);
sp.MouseLeave += new MouseEventHandler(l_MouseLeave);
sp.PreviewMouseWheel += new MouseWheelEventHandler(l_PreviewMouseWheel);
sp.PreviewKeyDown += new KeyEventHandler(sp_PreviewKeyDown);
if (Orientation == Orientation.Vertical)
{
sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
HorizontalContentAlignment = HorizontalAlignment.Stretch;
}
else
{
sv.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
VerticalContentAlignment = VerticalAlignment.Stretch;
}
scroll_timerClock.Interval = new TimeSpan(0, 0, 0, 0, 1);
decelerateEventHandler = new EventHandler(scroll_timerClock_Tick);
adjustEventHandler = new EventHandler(adjust_timerClock_Tick);
scroll_timerClock.Tick += decelerateEventHandler;
LayoutUpdated += new EventHandler(l_LayoutUpdated);
PreviewMouseDown += new MouseButtonEventHandler(SmoothScrollViewer_MouseDown);
}
//prin interactiunea cu mouse-ul asupra listei, se initiaza o actiune de
//selectie non-externa
void SmoothScrollViewer_MouseDown(object sender, MouseButtonEventArgs e)
{
external = false;
}
//functie de scroll la element specificat
public void ScrollTo(object item)
{
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
int indexOfItem = Items.IndexOf(item);
int nrItemsToMove = 0,
i = 0;
double cummulativeSize = 0;
do
{
lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(i));
cummulativeSize += (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
i++;
nrItemsToMove++;
}
while (cummulativeSize <= ((Orientation == Orientation.Vertical) ? sv.ViewportHeight : sv.ViewportWidth));
if (indexOfItem > Items.Count - nrItemsToMove || indexOfItem == 0)
{
for (i = 0; i <= nrItemsToMove; i++)
{
object elt = Items[0];
Items.RemoveAt(0);
Items.Add(elt);
}
indexOfItem = Items.IndexOf(item);
}
double scrollAmount = 0;
for (i = 0; i < indexOfItem - 1; i++)
{
lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(i));
scrollAmount += (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
}
if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scrollAmount);
else sv.ScrollToHorizontalOffset(scrollAmount);
selectionAllowed = true;
SelectedItem = item;
selectionAllowed = false;
scroll_out = false;
}
}
//Manipularea listei din tastele sageti
void sp_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (((e.Key == Key.Right || e.Key == Key.Left) && Orientation == Orientation.Horizontal) || ((e.Key == Key.Up || e.Key == Key.Down) && Orientation == Orientation.Vertical))
{
adjustLimit = 0;
scrollLessThanZero = false;
scrollMoreThanEnd = false;
SelectedIndex = -1;
backOffset = ((Orientation == Orientation.Vertical) ? (sv.ExtentHeight - sv.ViewportHeight) : (sv.ExtentWidth - sv.ViewportWidth));
scroll_mouseDown = false;
scroll_v0 = (e.Key == Key.Right || e.Key == Key.Up ? -1 : 1);
scroll_t0 = Environment.TickCount;
scroll_direction = (scroll_v0 >= 0);
scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);
scroll_timerClock.IsEnabled = true;
}
else
if (e.Key == Key.Return && stopWindow == null)
{
UIElement elt = InputHitTest(new Point(ActualWidth / 2, ActualHeight / 2)) as UIElement;
if (elt != null)
{
lbi = getListBoxItem(elt);
selectionAllowed = true;
SelectedIndex = ItemContainerGenerator.IndexFromContainer(lbi);
}
}
e.Handled = true;
}
//la ficare actualizare a randarii controlului, se initializeaza/redimensioneaza chenarul
//de oprire a elementului selectabil si campul editabil de filtrare in cazul in care acesta
//exista
void l_LayoutUpdated(object sender, EventArgs e)
{
try
{
if (StopWindowOffset >= 0)
{
lbi = (ListBoxItem)(ItemContainerGenerator.ContainerFromIndex(((SelectedIndex > 0) ? SelectedIndex : selectedIndex)));
hitOffset = (Orientation == Orientation.Vertical) ? lbi.ActualHeight / 4 : lbi.ActualWidth / 4;
stopWindowCoord = StopWindowOffset * ((Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth);
if (stopWindow == null)
{
stopWindow = new Rectangle();
stopWindow.SetValue(Grid.RowProperty, 1);
stopWindow.HorizontalAlignment = HorizontalAlignment.Left;
stopWindow.VerticalAlignment = VerticalAlignment.Top;
stopWindow.Fill = BorderBrush;
stopWindow.Opacity = 0.5;
stopWindow.IsHitTestVisible = false;
g.Children.Add(stopWindow);
}
stopWindow.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, stopWindowCoord, 0, 0) : new Thickness(stopWindowCoord, 0, 0, 0);
stopWindow.Width = lbi.ActualWidth;
stopWindow.Height = lbi.ActualHeight;
}
if (CanFilter)
{
if (filterEdit == null)
{
filterEdit = new TextBox();
filterEdit.HorizontalAlignment = HorizontalAlignment.Center;
filterEdit.VerticalAlignment = VerticalAlignment.Top;
filterEdit.Margin = new Thickness(3);
filterEdit.TextChanged += new TextChangedEventHandler(filterEdit_TextChanged);
filterEdit.PreviewKeyDown += new KeyEventHandler(filterEdit_KeyDown);
g.Children.Add(filterEdit);
}
filterEdit.Width = ActualWidth - 20;
}
}
catch (Exception) { }
}
//La apasarea tastei sageata jos in filtru, se coboara in lista
void filterEdit_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down)
{
sp.Focusable = true;
Keyboard.Focus(sp);
}
}
//textul dupa care are loc filtrarea
void filterEdit_TextChanged(object sender, TextChangedEventArgs e)
{
Items.Filter = new Predicate<object>(PassesFilter);
}
public bool PassesFilter(Object value)
{
if (filterEdit.Text != "")
{
Regex regex = new Regex("^" + Regex.Escape(filterEdit.Text).Replace("\\*", ".*").Replace("\\?", ".") + ".*$");
return regex.IsMatch(extractText(value as UIElement));
}
else return true;
}
//tratarea evenimentului de selectie asupra unui element din lista
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
//MessageBox.Show(selectedIndex+" "+SelectedIndex);
if (!external)
{
//MessageBox.Show(SelectedIndex + "");
if (!selectionAllowed)
{
if (SelectedIndex != -1)
{
selectedIndex = SelectedIndex;
SelectedIndex = -1;
}
}
}
else selectionAllowed = false;
external = true;
base.OnSelectionChanged(e);
}
//Extrage textul care apare in interiorul oricarui control
private string extractText(UIElement item)
{
if (item is TextBlock) return ((TextBlock)item).Text;
if (item is TextBox) return ((TextBox)item).Text;
if (item is ContentControl)
{
object content = ((ContentControl)item).Content;
if (content is UIElement) return extractText(content as UIElement);
else return content.ToString();
}
else
{
string result = "";
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(item); i++)
result += extractText(VisualTreeHelper.GetChild(item, i) as UIElement) + " ";
return result;
}
}
//scroll prin rotita mouse-ului
void l_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
sp.Focusable = true;
Keyboard.Focus(sp);
adjustLimit = 0;
scrollLessThanZero = false;
scrollMoreThanEnd = false;
SelectedIndex = -1;
backOffset = ((Orientation == Orientation.Vertical) ? (sv.ExtentHeight - sv.ViewportHeight) : (sv.ExtentWidth - sv.ViewportWidth));
scroll_mouseDown = false;
scroll_v0 = -e.Delta / 100;
scroll_t0 = Environment.TickCount;
scroll_direction = (scroll_v0 >= 0);
scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);
scroll_timerClock.IsEnabled = true;
}
//la parasirea controlului de catre cursorul mouse-ului, cu butonul apasat, se simuleaza
//invocarea unui eveniment de eliberare a butonului mouse-ului
private void l_MouseLeave(object sender, MouseEventArgs e)
{
if (scroll_mouseDown)
{
MouseButtonEventArgs e1 = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left);
e1.RoutedEvent = MouseLeaveEvent;
l_MouseUp(sender, e1);
}
}
//scroll controlat de miscarea mouse-ului
private void l_MouseMove(object sender, MouseEventArgs e)
{
if (scroll_mouseDown)
{
scroll_offset = scroll_init - ((Orientation == Orientation.Vertical) ? e.GetPosition(this).Y : e.GetPosition(this).X);
if (scroll_offset < 0)
if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, -scroll_offset, 0, 0) : new Thickness(-scroll_offset, 0, 0, 0);
else
{
if (canUpdate)
{
object elt = Items[Items.Count - 1];
Items.RemoveAt(Items.Count - 1);
Items.Insert(0, elt);
lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(Items.Count - 1);
double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
scroll_init += adjust;
canUpdate = false;
}
}
else
if (scroll_offset - backOffset > 0)
if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, 0, 0, scroll_offset - backOffset) : new Thickness(0, 0, scroll_offset - backOffset, 0);
else
{
if (canUpdate)
{
object elt = Items[0];
Items.RemoveAt(0);
Items.Add(elt);
lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(0);
double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.ActualWidth;
scroll_init -= adjust;
canUpdate = false;
}
}
else canUpdate = true;
if ((Orientation == Orientation.Vertical)) sv.ScrollToVerticalOffset(scroll_offset);
else sv.ScrollToHorizontalOffset(scroll_offset);
}
}
//comportamentul la eliberarea butonului mouse-ului; daca miscarea efectuata este una
//minora, se recurge la selectarea elementului vizat, altfel se continua cu initierea
//miscarii inertiale induse de acceleratia impusa de miscarea pana in acest moment
private void l_MouseUp(object sender, MouseButtonEventArgs e)
{
double move_offset = move_init - ((Orientation == Orientation.Vertical) ? e.GetPosition(this).Y : e.GetPosition(this).X);
selectionAllowed = (Math.Abs(move_offset) <= 10 && SelectedIndex != selectedIndex && StopWindowOffset < 0);
if (selectionAllowed) SelectedIndex = selectedIndex;
adjustLimit = 0;
if (scroll_mouseDown && move_offset != 0)
{
scroll_mouseDown = false;
scroll_span = Environment.TickCount - scroll_t0;
if (scroll_span > 0) scroll_v0 = move_offset / scroll_span;
else scroll_v0 = 0;
scroll_t0 = Environment.TickCount;
scroll_direction = (move_offset >= 0);
scroll_offset0 = ((Orientation == Orientation.Vertical) ? sv.VerticalOffset - sp.Margin.Top : sv.HorizontalOffset - sp.Margin.Left);
scroll_timerClock.IsEnabled = true;
}
else
{
scroll_mouseDown = false;
if (move_offset == 0) scrollStopped();
}
}
//timer-ul responsabil cu actualizarea deplasamentului de derulare in urma miscarii
//uniform incetinite de dupa eliberarea butonului mouse-ului
private void scroll_timerClock_Tick(object sender, EventArgs e)
{
double scroll_a = (scroll_direction ? -1 : 1) * ((scroll_out) ? 0.005 : 0.003),
scroll_t = Environment.TickCount - scroll_t0,
scroll = 0.5 * scroll_a * scroll_t * scroll_t + scroll_v0 * scroll_t + scroll_offset0,
scroll_v = scroll_a * scroll_t + scroll_v0;
if (scroll > 0 && scroll < Math.Abs(backOffset))
if (scroll_out)
{
sp.Margin = new Thickness(0, 0, 0, 0);
scroll_out = false;
scrollStopped();
}
else
{
if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scroll);
else sv.ScrollToHorizontalOffset(scroll);
if ((scroll_v <= 0) == scroll_direction) scrollStopped();
}
else
if (ScrollLimitBehaviour == ScrollLimitBehaviour.Springs)
{
if (scroll < 0) scrollLessThanZero = true;
else scrollMoreThanEnd = true;
if (scrollLessThanZero && scrollMoreThanEnd)
{
sp.Margin = new Thickness(0, 0, 0, 0);
scrollLessThanZero = false;
scrollMoreThanEnd = false;
scroll_out = false;
scrollStopped();
}
else
{
scroll_out = true;
if (scroll > 0) sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, 0, 0, scroll - backOffset) : new Thickness(0, 0, scroll - backOffset, 0);
else sp.Margin = (Orientation == Orientation.Vertical) ? new Thickness(0, -scroll, 0, 0) : new Thickness(-scroll, 0, 0, 0);
if (Orientation == Orientation.Vertical) sv.ScrollToVerticalOffset(scroll);
else sv.ScrollToHorizontalOffset(scroll);
}
}
else
{
scroll_out = false;
if (scroll <= 0)
{
object elt = Items[Items.Count - 1];
Items.RemoveAt(Items.Count - 1);
Items.Insert(0, elt);
lbi = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(Items.Count - 1);
double adjust = (Orientation == Orientation.Vertical) ? lbi.ActualHeight : lbi.A