Horizontal dynamic listbox with insert buttons - c#

I would like to create an horizontal dynamic listbox:
A button is visible when the mouse is between two items.
<ListBox
MinHeight="32"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
SelectionMode="Extended"
HorizontalAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>
<TextBlock><Run Text="C1" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C2" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C3" /></TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock><Run Text="C4" /></TextBlock>
</ListBoxItem>
</ListBox>
</ListBox>
Any suggestions, please?
Thank you
EDIT
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var border1 = (Border)item.FindName("HitTestBorder1");
var border2 = (Border)item.FindName("HitTestBorder2");
if (border1 is Border)
{
var margin = border1.Margin;
margin.Left = -item.ActualWidth;
border1.Margin = margin;
}
if (border2 is Border)
{
var margin = border2.Margin;
margin.Left = item.ActualWidth;
border2.Margin = margin;
}
}
}
The problem is that the grid width is resized after mouse_enter... So, I don't get the overlay "effect".

Here is the solution.
I created a canvas. It contains 2 borders with button inside and ZPanel is setted very high (=1000) to be on top of everything.
I change the position of these borders on MouseEnter and MouseLeave events of ListBoxItem (see ItemTemplate).
XAML
<Border Padding="10">
<Canvas x:Name="supergrid" MouseLeave="supergrid_MouseLeave">
<Border x:Name="HitTestBorder1"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="0,0,0,0" >
<Button x:Name="button1"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Red"
Click="button1_Click"
/>
</Border>
<Border x:Name="HitTestBorder2"
Panel.ZIndex="1000"
Background="Transparent"
BorderBrush="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="1,1,1,1" >
<Button x:Name="button2"
Visibility="Hidden"
Content="+"
Height="18" Width="18" FontSize="6" Background="Green"
Click="button2_Click"
/>
</Border>
<ListBox x:Name="HorizontalListBox"
ItemsSource="{Binding DataModels}"
Margin="0,0,0,0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="Yellow" MouseLeave="HorizontalListBox_MouseLeave">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!--<Setter Property="MinWidth" Value="60" />
<Setter Property="MinHeight" Value="40" />-->
<Setter Property="Background" Value="Blue" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="myElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseEnter="myElement_MouseEnter"
MouseLeave="myElement_MouseLeave"
Background="White">
<TextBlock x:Name="myText"
Margin="10"
Text="{Binding TextValue}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
</Border>
C#
public partial class MainWindow : Window
{
private DataModel currentDataModel;
public ObservableCollection<DataModel> DataModels { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.DataModels = new ObservableCollection<DataModel>();
this.DataModels.Add(new DataModel("Item1"));
this.DataModels.Add(new DataModel("Item2"));
this.DataModels.Add(new DataModel("SuperMegaHyperLong"));
this.DataModels.Add(new DataModel("Item3"));
this.DataModels.Add(new DataModel("Item4"));
this.DataModels.Add(new DataModel("123"));
this.DataModels.Add(new DataModel("Item5"));
}
private void myElement_MouseEnter(object sender, MouseEventArgs e)
{
updateOverlay((Grid)sender);
}
private void updateOverlay(Grid lbi)
{
if (lbi is Grid item) //the grid of listboxitem
{
if (item.DataContext is DataModel data)
{
Debug.WriteLine("myElement_MouseEnter: " + data.TextValue);
}
var myText = (TextBlock)item.FindName("myText");
if (myText is TextBlock)
{
Point relativePoint = myText.TransformToAncestor(supergrid)
.Transform(new Point(0, 0));
Debug.WriteLine("relativePoint: " + relativePoint.ToString());
//update left button position
double w = button1.ActualWidth;
double h = button1.ActualHeight;
double x = relativePoint.X - w / 2.0 - myText.Margin.Left;
double y = relativePoint.Y;
updateMargin(HitTestBorder1,
x,
x + w,
y + h,
y);
//update right button position
w = button2.ActualWidth;
h = button2.ActualHeight;
x = relativePoint.X - w / 2.0 - myText.Margin.Left;
x += item.ActualWidth;
y = relativePoint.Y;
updateMargin(HitTestBorder2,
x,
x + w,
y + h,
y);
//show the button
button1.Visibility = button2.Visibility = Visibility.Visible;
//the current item
if (myText.DataContext is DataModel dm)
{
this.currentDataModel = dm;
}
}
}
}
private void updateMargin(Border border, double left, double right, double bottom, double top)
{
//border = HitTestBorder2;
var margin = border.Margin;
margin.Left = left;
margin.Right = right;
margin.Top = top;
margin.Bottom = bottom;
border.Margin = margin;
Debug.WriteLine("updateMargin Left: " + left.ToString() + "Right: " + right.ToString());
}
private void myElement_MouseLeave(object sender, MouseEventArgs e)
{
//change button visibility
if (sender is Grid item)
{
if (item.DataContext is DataModel data)
{
System.Diagnostics.Debug.WriteLine("myElement_MouseLeave: " + data.TextValue);
}
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
private void HorizontalListBox_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("HorizontalListBox_MouseLeave");
//button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void supergrid_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("supergrid_MouseLeave: ");
button1.Visibility = button2.Visibility = Visibility.Hidden;
// => :D
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("LEFT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
DataModel newDataModel = new DataModel($"Item{this.DataModels.Count}");
this.DataModels.Insert(idx, newDataModel);
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("RIGHT INSERT");
int idx = this.DataModels.IndexOf(currentDataModel);
this.DataModels.Insert(idx+1, new DataModel($"Item{this.DataModels.Count}"));
button1.Visibility = button2.Visibility = Visibility.Hidden;
}
}
public class DataModel
{
public string TextValue { get; set; }
public DataModel(string textValue)
{
this.TextValue = textValue;
}
}
As you can see, there is place for improvements (like select the new ListBoxItem). Disable the buttons when we drag, animation, delay, etc.

Related

wpf c# datagrid GroupStyle - which rows are expanded

I have the following WPF DataGrid with GroupStyle. I need to now which rows are expanded when I have expanded/collapsed event.
I add:
Expanded="Expander_Process" Collapsed="Expander_Process"
but in the event function Expander_Process when I try to get the row
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
then the row is null. So my question is: how can I know which rows are expanded in the datagrid?
<DataGrid x:Name="gvOptionChain" AutoGenerateColumns="False" FontWeight="Bold" Background="#FF262626" Foreground="White" Width="1509"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
Margin="53,120,89,4.333" HorizontalContentAlignment="Right" SelectionChanged="gvOptionChain_SelectionChanged" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.ScrollUnit ="Item" VirtualizingPanel.VirtualizationMode="Recycling" EnableRowVirtualization="True" EnableColumnVirtualization = "True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionUnit="Cell" SelectedItem="{Binding SelectedItem, Mode=OneWay}" >
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="False" Foreground="#FFEEEEEE" Expanded="Expander_Process" Collapsed="Expander_Process" >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupItem}}, Converter={StaticResource ResourceKey=groupToTitleConverter}}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
{
}
}
}
on the expander event - get the rows that expanded throu
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
and update a flag about the IsExpanded status of the row . this event happens many times for each expand - so I add timer to process the rows only once
private System.Timers.Timer ExpandTimer;
private bool bIsTimerOn = false;
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bIsTimerOn == false)
{
ExpandTimer.Start();
bIsTimerOn = true;
}
}
}
a better answer - activate the timer only if there is new expand status :
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
bool bNewStatus = false;
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
if (bNewStatus == false)
{
bNewStatus = ScrtyData1.IsExpanded != expander.IsExpanded;
}
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bNewStatus == true)
{
ExpandTimer.Start();
}
}
}

Adding Avg, Min and Max Line to Scatter Chart

I am using a ScatterDataPoint chart in wpf,C#,.NET 3.5 and I have to add 3 horizontal lines for minimum, maximum and average value line that can be different depending on the values that I introduce.
Basically I take some values from a DB and I have to display them in a chart, below you have the basic code from which I started to create my application, but I do not know hoe to add those 3 lines.
here is the XAML code :
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0,-28,0,28">
<Grid Height="921" Background="WhiteSmoke">
<chartingToolkit:Chart Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" VerticalAlignment="Top" Margin="16,36,20,0" Height="800" IsEnabled="True">
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}">
<chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:LinearAxis Orientation="X" Title="Time (Mins)" Interval="5" />
</chartingToolkit:ScatterSeries.IndependentAxis>
<chartingToolkit:ScatterSeries.DependentRangeAxis>
<chartingToolkit:LinearAxis Orientation="Y" Title="Lenght" x:Name="Yaxis"/>
</chartingToolkit:ScatterSeries.DependentRangeAxis>
</chartingToolkit:ScatterSeries>
</chartingToolkit:Chart>
</Grid>
</ScrollViewer>
</Grid>
</Window>
Here is the code behind, now it just generates a random point:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
ObservableCollection<KeyValuePair<double, double>> Power = new ObservableCollection<KeyValuePair<double, double>>();
public MainWindow()
`enter code here` {
InitializeComponent();
showColumnChart();
timer.Interval = new TimeSpan(0,0,0,0,1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
Power.Add(new KeyValuePair<double, double>(i, random.NextDouble()));
i += 1;
if(Power.Count==500)
{
timer.IsEnabled = false;
}
}
private void showColumnChart()
{
lineChart.DataContext = Power;
}
}
}
Normally you would use a StripLine for that, but the toolkit doesn't seem to have one. So use an extra LineSeries instead:
XAML:
<Window x:Class="WpfApplication336.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:local="clr-namespace:WpfApplication336"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:ScatterDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:ScatterDataPoint">
<Viewbox x:Name="viewbox">
<Ellipse Width="1px" Height="1px" Fill="Black"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="3"/>
<Setter Property="Height" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<chartingToolkit:Chart Grid.Row="0" Name="lineChart" Title="Power Graph" Background="WhiteSmoke" Foreground="Black" IsEnabled="True">
<chartingToolkit:Chart.Series>
<chartingToolkit:ScatterSeries Title="Points" ItemsSource="{Binding Power}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" IsSelectionEnabled="True" DataPointStyle="{StaticResource BubbleDataPointStyle}"/>
<chartingToolkit:LineSeries Title="Average" ItemsSource="{Binding PowerAvg}" DependentValueBinding="{Binding Path=Value}" IndependentValueBinding="{Binding Path=Key}" />
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
<Button Grid.Row="1" Click="Button_Click">START</Button>
</Grid>
ViewModel:
public class MyViewModel
{
public ObservableCollection<KeyValuePair<double, double>> Power { get; set; }
public ObservableCollection<KeyValuePair<double, double>> PowerAvg { get; set; }
public MyViewModel()
{
Power = new ObservableCollection<KeyValuePair<double, double>>();
PowerAvg = new ObservableCollection<KeyValuePair<double, double>>();
}
public void Add(double x, double y)
{
Power.Add(new KeyValuePair<double, double>(x, y));
double xmin = Power.Min(kvp => kvp.Key);
double xmax = Power.Max(kvp => kvp.Key);
double ymin = Power.Min(kvp => kvp.Value);
double ymax = Power.Max(kvp => kvp.Value);
double yavg = Power.Average(kvp => kvp.Value);
PowerAvg.Clear();
PowerAvg.Add(new KeyValuePair<double, double>(xmin, yavg));
PowerAvg.Add(new KeyValuePair<double, double>(xmax, yavg));
}
}
MainWindow:
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
MyViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new MyViewModel();
DataContext = vm;
//showColumnChart();
timer.Interval = new TimeSpan(0, 0, 0, 0, 1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
//timer.IsEnabled = true;
}
double i = 1;
Random random = new Random();
void timer_Tick(object sender, EventArgs e)
{
vm.Add(i, random.NextDouble());
i += 1;
if (vm.Power.Count == 250)
{
timer.Stop();
}
}
private void showColumnChart()
{
lineChart.DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
timer.Start();
}
}
You can easily add extra LineSeries for min and max just like we did for the average.

C# WPF - how to prevent image to be dragged outside canvas

i tried this Answer and implement it to my code, and here is my code :
XAML
<Window x:Class="DSLayout.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:tk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:DSLayout"
Title="MainWindow" Height="720" Width="1280" ResizeMode="NoResize">
<Window.Resources>
<local:BrushColorConverter x:Key="BrushColorConverter"/>
</Window.Resources>
<Grid>
<Border BorderBrush="Silver" BorderThickness="1" Name="borderHeader" Margin="12,12,12,636" />
<Border BorderBrush="Silver" BorderThickness="1" Name="borderEditor" Margin="12,69,850,12">
<Canvas>
<Border BorderBrush="Silver" BorderThickness="1" Canvas.Left="24" Canvas.Top="20" Height="59" Name="border1" Width="355"></Border>
<Canvas Height="59" Canvas.Left="26" Canvas.Top="20"></Canvas>
<TextBlock Name="textBlock1" Text="Color Palette" Height="31" Width="197" FontSize="22" Canvas.Left="40" Canvas.Top="34" />
<tk:ColorPicker x:Name="ColorPalette" ColorMode="ColorCanvas"
SelectedColor="{Binding ElementName=Layout,
Path=Background,
Converter={StaticResource BrushColorConverter}}"
Canvas.Left="243" Canvas.Top="34" Height="31" />
<Button Grid.Row="1" Content="Add Image" Click="AddButtonClick" Canvas.Left="24" Canvas.Top="100" Height="43" Width="104" />
<Border BorderBrush="Silver" BorderThickness="1" Canvas.Left="24" Canvas.Top="227" Height="350" Name="border3" Width="355">
<Canvas>
<TextBlock Canvas.Left="15" Canvas.Top="27" Height="23" Name="textBlock2" Text="X-Pos " FontSize="16" Width="53" />
<TextBlock Canvas.Left="174" Canvas.Top="27" FontSize="16" Height="23" Name="textBlock3" Text="Y-Pos " Width="53" />
<Label Name="posX" Height="23" Width="83" Canvas.Left="74" Canvas.Top="27" />
<Label Name="posY" Canvas.Left="233" Canvas.Top="27" Height="23" Width="83" />
<TextBlock Canvas.Left="15" Canvas.Top="68" FontSize="16" Height="23" Name="textBlock4" Text="Width" Width="53" />
<TextBlock Canvas.Left="174" Canvas.Top="68" FontSize="16" Height="23" Name="textBlock5" Text="Height" Width="53" />
<Label Name="imgHeight" Canvas.Left="74" Canvas.Top="68" Height="23" Width="83" />
<Label Name="imgWidth" Canvas.Left="233" Canvas.Top="68" Height="23" Width="83" />
<!--<TextBlock Canvas.Left="15" Canvas.Top="169" FontSize="16" Height="23" Name="textBlock4" Text="Height" Width="53" />
<TextBlock Canvas.Left="174" Canvas.Top="169" FontSize="16" Height="23" Name="textBlock5" Text="Width" Width="53" />
<TextBox Canvas.Left="74" Canvas.Top="169" Height="23" Name="textBox3" Width="83" />
<TextBox Canvas.Left="233" Canvas.Top="169" Height="23" Name="textBox4" Width="83" />-->
</Canvas>
</Border>
</Canvas>
</Border>
<Border BorderBrush="Silver" BorderThickness="1" Name="borderLayout" Margin="446,69,12,12">
<Canvas x:Name="Layout" Background="White" AllowDrop="True" ClipToBounds="True"
MouseLeftButtonDown="MouseLeftButtonDown"
MouseLeftButtonUp="MouseLeftButtonUp"
MouseMove="MouseMove">
</Canvas>
</Border>
</Grid>
CS
namespace DSLayout
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public int imgX { get; set; }
public int imgY { get; set; }
private void AddButtonClick(object sender, RoutedEventArgs e)
{
var dialog = new Microsoft.Win32.OpenFileDialog();
dialog.Filter =
"Image Files (*.jpg; *.png; *.jpeg; *.gif; *.bmp)|*.jpg; *.png; *.jpeg; *.gif; *.bmp";
if ((bool)dialog.ShowDialog())
{
var bitmap = new BitmapImage(new Uri(dialog.FileName));
var image = new Image { Source = bitmap };
this.imgX = bitmap.PixelWidth;
this.imgY = bitmap.PixelWidth;
Canvas.SetLeft(image, 0);
Canvas.SetTop(image, 0);
Layout.Children.Add(image);
imgHeight.Content = bitmap.PixelHeight;
imgWidth.Content = bitmap.PixelWidth;
}
}
private Image draggedImage;
private Point mousePosition;
private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var image = e.Source as Image;
if (image != null && Layout.CaptureMouse())
{
mousePosition = e.GetPosition(Layout);
draggedImage = image;
Panel.SetZIndex(draggedImage, 1);
}
}
private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (draggedImage != null)
{
Layout.ReleaseMouseCapture();
Panel.SetZIndex(draggedImage, 0);
draggedImage = null;
}
}
private void MouseMove(object sender, MouseEventArgs e)
{
if (draggedImage != null)
{
var position = e.GetPosition(Layout);
var offset = position - mousePosition;
mousePosition = position;
if (mousePosition.X > 0 && mousePosition.Y > 0)
{
Canvas.SetLeft(draggedImage, Canvas.GetLeft(draggedImage) + offset.X);
Canvas.SetTop(draggedImage, Canvas.GetTop(draggedImage) + offset.Y);
}
posX.Content = Canvas.GetLeft(draggedImage);
posY.Content = Canvas.GetTop(draggedImage);
}
}
}
}
so i tried using the ClipToBound="True" but it'll missing if i drag to outside the canvas. so i tried to limit it using if (mousePosition.X > 0 && mousePosition.Y > 0) works but not that i want because it will go outside the canvas if i drag it to the left and i drag from the right point of the image.
my idea is to make the draggedImage to be my cursor so with if (mousePosition.X > 0 && mousePosition.Y > 0) it will prevent draggedImage to go outside the canvas. is that possible to do that?
or any simple idea to solve this?
EDIT :
i tried using this code but it works but its not really good because when i drag it out side the canvas it'll move like bouncy from pos -1 to 0.
if (Canvas.GetLeft(draggedImage) <= 0)
{
Canvas.SetLeft(draggedImage, 0);
}
if (Canvas.GetTop(draggedImage) <= 0)
{
Canvas.SetTop(draggedImage, 0);
}
if (Canvas.GetLeft(draggedImage) + this.imgX >= 800)
{
Canvas.SetLeft(draggedImage, 800 - this.imgX);
}
if (Canvas.GetTop(draggedImage) +this.imgY >= 600)
{
Canvas.SetTop(draggedImage, 600 - this.imgY);
}
When you drag an item you change it's X and Y position by specifying Canvas's attached properties Canvas.Left and Canvas.Top. So it is easy to ensure that the dragged element does not get dragged outside it's panel.
double canvasSize = 800;
double newLeft = Canvas.GetLeft(draggedImage) + offset.X;
double newTop = Canvas.GetTop(draggedImage) + offset.Y;
if (newLeft < 0)
newLeft = 0;
else if (newLeft + draggedImage.ActualWidth > canvasSize)
newLeft = canvasSize - draggedImage.ActualWidth;
if (newTop < 0)
newTop = 0;
else if (newTop + draggedImage.ActualHeight > canvasSize)
newTop = canvasSize - draggedImage.ActualHeight;
Canvas.SetLeft(draggedImage, newLeft);
Canvas.SetTop(draggedImage, newTop);
This will check if the element you drag is going outside of the Canvas.

WPF View not Updated position after update Model

I have next structure:
Main window
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Margin="100 70 0 0">
<local:RotateControl x:Name="rtc1" Panel.ZIndex="1"
Width="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource SizeConverter}}"
Height="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource SizeConverter}}"
Radius="{Binding ElementName=window, Path=ActualHeight, Converter={StaticResource RadiusConverter}}"
Loaded="rtc1_Loaded" SizeChanged="rtc1_SizeChanged"/>
</Grid>
<StackPanel HorizontalAlignment="Right" Canvas.Right="80" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="year" Width="40"></TextBox>
<TextBox x:Name="month" Width="20"></TextBox>
<TextBox x:Name="day" Width="20"></TextBox>
<TextBox x:Name="hour" Width="20"></TextBox>
<TextBox x:Name="min" Width="20"></TextBox>
<TextBox x:Name="sec" Width="20"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="lat" Width="80" Text="55.75"></TextBox>
<TextBox x:Name="lng" Width="80" Text="37.583333"></TextBox>
</StackPanel>
<ComboBox x:Name="cbHSys" Width="300" SelectedIndex="0"></ComboBox>
<Button x:Name="GatData" Click="GatData_Click">Get data</Button>
<ScrollViewer>
<Label x:Name="julday"></Label>
</ScrollViewer>
</StackPanel>
</Canvas>
Custom control
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Astro"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<local:XLineCoordConverter x:Key="XLineCoordConverter" />
<local:YLineCoordConverter x:Key="YLineCoordConverter" />
<SolidColorBrush x:Key="Blue1Brush" Color="#e2edfa" />
<SolidColorBrush x:Key="Blue2Brush" Color="#0080c0" />
<Pen x:Key="BlackPen1" Thickness="1" Brush="Black"></Pen>
<Pen x:Key="BluePen1" Thickness="0.1" Brush="{StaticResource Blue2Brush}"></Pen>
<Style TargetType="{x:Type local:RotateControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:RotateControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="Transparent">
<Line X1="{Binding Path=Radius}" Y1="{Binding Path=Radius}" Stroke="White" StrokeThickness="1">
<Line.X2>
<MultiBinding Converter="{StaticResource XLineCoordConverter}" ConverterParameter="1">
<Binding Path="Radius"/>
<Binding Path="House1"/>
<Binding Path="House2"/>
</MultiBinding>
</Line.X2>
<Line.Y2>
<MultiBinding Converter="{StaticResource YLineCoordConverter}" ConverterParameter="1">
<Binding Path="Radius"/>
<Binding Path="House1"/>
<Binding Path="House2"/>
</MultiBinding>
</Line.Y2>
</Line>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And model:
public class CircleModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
double _radius;
public double Radius
{
get
{
return _radius;
}
set
{
if (_radius == value) return;
_radius = value;
OnPropertyChanged("Radius");
}
}
double _angel;
public double Angel
{
get
{
return _angel;
}
set
{
if (_angel == value) return;
_angel = value;
OnPropertyChanged("Angel");
}
}
double _house1;
public double House1
{
get
{
return _house1;
}
set
{
if (_house1 == value) return;
_house1 = value;
OnPropertyChanged("House1");
}
}
...................
}
And code for change data:
private void GetData()
{
julday.Content = "";
//SetCurDate();
DateTime date = new DateTime(Convert.ToInt32(year.Text), Convert.ToInt32(month.Text), Convert.ToInt32(day.Text),
Convert.ToInt32(hour.Text), Convert.ToInt32(min.Text), Convert.ToInt32(sec.Text));
CalcNatalChart(date);
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
}
private void GatData_Click(object sender, RoutedEventArgs e)
{
GetData();
}
private void rtc1_Loaded(object sender, RoutedEventArgs e)
{
GetData();
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1.0);
_timer.Tick += timer_Tick;
_timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
julday.Content = "";
CalcNatalChart(DateTime.Now);
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
}
All data in the form of updated correctly but View not. Only if press button GatData_Click. Dynamically change the data does not work.
Can you help me to solve it problem? Thanks
The problem is in these lines:
_model.Radius = (this.ActualHeight - (this.ActualHeight > 150 ? 150 : 0)) / 2;
this.ViewModel = _model;
After you reassign ViewModel, the view doesn't know about this change and uses old object instance. Either initialize ViewModel only once in constructor and then change it's properties or make that ViewModel property also observable so that you could notify when the data is changed.
Try this one...
instead of your this.ViewModel = _model; put this.ViewModel.Radious = _model.Radious;

Crop Image using Canvas Rectangle

Cropping the image is not working properly. Where I'm wrong?
My Xaml :
<Grid x:Name="Gridimage1">
<Image Name="image1" Grid.Column="0" Height="317" HorizontalAlignment="Left" Margin="20,67,0,0" Stretch="Fill" VerticalAlignment="Top" Width="331"></Image>
<Canvas x:Name="BackPanel">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed" />
</Canvas>
</Grid>
<Button Content=">>" Height="23" HorizontalAlignment="Left" Margin="357,201,0,0" Name="Go" VerticalAlignment="Top" Width="41" Click="Go_Click" FontWeight="Bold" Visibility="Hidden" />
<Image Grid.Column="1" Height="317" HorizontalAlignment="Left" Margin="408,67,0,0" Name="image2" Stretch="Fill" VerticalAlignment="Top" Width="331" />
C# :
private bool isDragging = false;
private Point anchorPoint = new Point();
public MainWindow()
{
InitializeComponent();
Gridimage1.MouseLeftButtonDown += new MouseButtonEventHandler(image1_MouseLeftButtonDown);
Gridimage1.MouseMove += new MouseEventHandler(image1_MouseMove);
Gridimage1.MouseLeftButtonUp += new MouseButtonEventHandler(image1_MouseLeftButtonUp);
Go.IsEnabled = false;
image2.Source = null;
}
private void Go_Click(object sender, RoutedEventArgs e)
{
if (image1.Source != null)
{
Rect rect1 = new Rect(Canvas.GetLeft(selectionRectangle), Canvas.GetTop(selectionRectangle), selectionRectangle.Width, selectionRectangle.Height);
System.Windows.Int32Rect rcFrom = new System.Windows.Int32Rect();
rcFrom.X = (int)((rect1.X) * (image1.Source.Width) /(image1.Width));
rcFrom.Y = (int)((rect1.Y) *(image1.Source.Height) / (image1.Height));
rcFrom.Width = (int)((rect1.Width) * (image1.Source.Width) /(image1.Width));
rcFrom.Height = (int)((rect1.Height) * (image1.Source.Height) /(image1.Height));
BitmapSource bs = new CroppedBitmap(image1.Source as BitmapSource, rcFrom);
image2.Source = bs;
}
}
#region "Mouse events"
private void image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
isDragging = true;
}
}
private void image1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void image1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if(selectionRectangle.Width >0)
{
Go.Visibility = System.Windows.Visibility.Visible;
Go.IsEnabled = true;
}
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void RestRect()
{
selectionRectangle.Visibility = Visibility.Collapsed;
isDragging = false;
}
#endregion
It is cropping the wrong part.
The Margin property was not set properly to the Canvas control. It should be the same as Image control's margin properly value. If we don't set Margin to Canvas, It will take the full window size.
Xaml
<Grid x:Name="Gridimage1" Margin="0,0,411,100">
<Image Name="image1" Grid.Column="0" Height="317" HorizontalAlignment="Left" Margin="20,67,0,0" Stretch="Fill" VerticalAlignment="Top" Width="331">
</Image>
<Canvas x:Name="BackPanel" Margin="20,67,0,0">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed" />
</Canvas>
</Grid>
<Grid x:Name="other">
<Button Content=">>" Height="23" HorizontalAlignment="Left" Margin="341,152,0,0" Name="Go" VerticalAlignment="Top" Width="41" Click="Go_Click" FontWeight="Bold" />
<Image Height="317" HorizontalAlignment="Left" Margin="403,10,-217,-7" Name="image2" Stretch="Fill" VerticalAlignment="Top" Width="331" />
</Grid>
</Grid>

Categories