OK, here's my XAML:
<Window x:Class="nathan___visual_studio_panes___layers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<Button Name="move_ellipse2_to_GridA" Click="move_ellipse2_to_GridA_Click">
Move ellipse2 to GridA
</Button>
<Button Name="move_ellipse3_to_GridA" Click="move_ellipse3_to_GridA_Click">
Move ellipse3 to GridA
</Button>
</StackPanel>
<Grid Grid.IsSharedSizeScope="True" Background="AliceBlue">
<Grid Name="gridA">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Grid.Row="0" Fill="CornflowerBlue"/>
</Grid>
<Grid Name="gridB" Visibility="Visible">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="1" Name="ellipse2" Fill="Aquamarine"/>
<GridSplitter Name="gridB_grid_splitter" Width="5" Grid.Column="1" HorizontalAlignment="Left"/>
</Grid>
<Grid Name="gridC" Visibility="Visible">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="1" Name="ellipse3" Fill="Cornsilk"/>
<GridSplitter Name="gridC_grid_splitter" Width="5" Grid.Column="1" HorizontalAlignment="Left"/>
</Grid>
</Grid>
</DockPanel>
</Window>
And here's the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace nathan___visual_studio_panes___layers
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ColumnDefinition GridA_column_for_ellipse2 = new ColumnDefinition();
ColumnDefinition GridA_column_for_ellipse3 = new ColumnDefinition();
public MainWindow()
{
InitializeComponent();
}
private void move_ellipse2_to_GridA_Click(object sender, RoutedEventArgs e)
{
gridB.Children.Remove(gridB_grid_splitter);
var i = gridA.ColumnDefinitions.Count;
gridA.ColumnDefinitions.Add(GridA_column_for_ellipse2);
gridB.Children.Remove(ellipse2);
gridA.Children.Add(ellipse2);
Grid.SetColumn(ellipse2, i);
var grid_splitter = new GridSplitter() { Width = 5, HorizontalAlignment = HorizontalAlignment.Left };
gridA.Children.Add(grid_splitter);
Grid.SetColumn(grid_splitter, i);
Console.WriteLine(i);
}
private void move_ellipse3_to_GridA_Click(object sender, RoutedEventArgs e)
{
gridC.Children.Remove(gridC_grid_splitter);
var i = gridA.ColumnDefinitions.Count;
gridA.ColumnDefinitions.Add(GridA_column_for_ellipse3);
gridC.Children.Remove(ellipse3);
gridA.Children.Add(ellipse3);
Grid.SetColumn(ellipse3, i);
var grid_splitter = new GridSplitter() { Width = 5, HorizontalAlignment = HorizontalAlignment.Left };
gridA.Children.Add(grid_splitter);
Grid.SetColumn(grid_splitter, i);
Console.WriteLine(i);
}
}
}
If I start the program and click the "Move ellipse2 to GridA" and "Move ellipse3 to GridA" buttons, I get "the right thing". I.e. the three ellipses and two grid splitters all appear in the window.
However, if I start the program, drag the grid splitter (which moves ellipse3), click "Move ellipse2 to GridA", drag the grid splitter for ellipse2 (the green one), and finally click "Move ellipse3 to GridA", ellipse3 disappears completely!
What am I doing wrong here? :-) I.e. pushing both buttons should lead to all three ellipses showing up in the window, regardless of any splitter dragging.
I know this is kind of a weird demonstration program. One of the examples in Adam Nathan's WPF Unleashed is a model of the Visual Studio start screen. The demo program above is just exploring a different technique for docking/undocking the panes.
Thanks for any hints or tips. I'm a WPF newb. :-)
The solution
Place following code at the end of move_ellipse3_to_GridA_Click method
foreach (ColumnDefinition column in gridA.ColumnDefinitions)
column.Width = new GridLength(1, GridUnitType.Star);
The explanation
The problem is that GridSplitter modifies ColumnDefinition.Width. It remaines star, but it becomes something like "357*". That's why the last column in gridA becomes vanishingly small. You can see it if you place breakpoint at the beginning of move_ellipse3_to_GridA_Click handler, reproduce your "bad" scenario and press that button second time. Check gridA.ColumnDefinitions[0] and [1] Width there.
Awareness
Just for making the world better: it is advised to place GridSplitter in a dedicated ColumnDefinition (RowDefinition) with Width (Height) set to Auto, and the GridSplitter's alignments to Center and Stretch.
Also there's no need to define RowDefinition or ColumnDefinition if there's only one of them and there's no need to set element's Row or Column to 0 - it is by default.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I want to add canvas elements by user input. Something like when a button is clicked, a new <Ellipse/> element is added to the XAML file, inside the Canvas.
<Canvas x:Name="GraphDisplayFrame" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="4">
<Ellipse
Width="50"
Height="50"
Stroke="Black"
StrokeThickness="2"
Canvas.Left="100"
Canvas.Top="100" />
</Canvas>
I'm new to WPF, i'm not sure if this is the right way to do this.
The other thing i'm trying is System.Windows.Media but manipulating the XAMl file looks easier and nicer, since then the locations of the drawings are anchored to the canvas. I'm not sure if i can achieve something similar with System.Windows.Media.
So my question is in the title, but I'm open to other suggestions.
You probably want to learn about Bindings in WPF. Let's say you want your Ellipses be added by user's input (e.g. on Button click) to your Canvas. I'm not sure about Canvas usage for that purpose (it hasn't auto-alignments for child elements), so I used WrapPanel instead (to allow it align items). And we need 2 Buttons (to Add and Remove Ellipses). And I add a Label to display current amount of Ellipses that we have.
XAML:
<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Name ="mainWindow"
Title="Main Window"
Width="800"
MaxWidth="800"
Height="450"
MaxHeight="450">
<Grid x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Label Content="{Binding ElementName=mainWindow, Path=EllipsesCount, UpdateSourceTrigger=PropertyChanged}"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Grid.Row="0"
Background="DimGray"
Foreground="White"
Margin="15,35" />
<Button x:Name="BtnAddEllipse"
Content="ADD ELLIPSE"
Grid.Row="1"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="LightGreen"/>
<Button x:Name="BtnRemoveEllipse"
Content="REMOVE ELLIPSE"
Grid.Row="2"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="IndianRed"/>
<WrapPanel Orientation="Horizontal"
Background="Gainsboro"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"
Grid.Row="0"
Grid.ColumnSpan="3"
Grid.RowSpan="4" >
<ItemsControl ItemsSource="{Binding ElementName=mainWindow, Path=Ellipses, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</Window>
Here you see that Label.Content property is binded to some EllipsesCount property (you'll see it in code-behind below). Also as WrapPanel is binded to Ellipses property.
Code-behind: (for copypaste purpose)
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
// Text for Label about Ellipses amount in collection
private object _ellipsesCount = "Current ellipses count: 0";
public object EllipsesCount
{
get => _ellipsesCount;
set
{
_ellipsesCount = "Current ellipses count: " + value;
// When we set new value to this property -
// we call OnPropertyChanged notifier, so Label
// would be "informed" about this change and will get new value
OnPropertyChanged(nameof(EllipsesCount));
}
}
// Collection for Ellipses
private ObservableCollection<Ellipse> _ellipses;
public ObservableCollection<Ellipse> Ellipses
{
get => _ellipses;
set
{
_ellipses = value;
OnPropertyChanged(nameof(Ellipses));
}
}
// Hanlder, which would notify our Controls about property changes, so they will "update" itself with new values
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Just for random colors
private readonly Random random = new Random();
public MainWindow()
{
InitializeComponent();
// Initialize collection of Ellipses
Ellipses = new ObservableCollection<Ellipse>();
// Handle when collection is changed to update Label
// with a new amount of Ellipses
Ellipses.CollectionChanged += delegate
{
// Update counter of ellipses when new one added or existing removed
EllipsesCount = Ellipses.Count;
};
BtnAddEllipse.Click += delegate
{
// Create an Ellipse with random stroke color
var ellipse = new Ellipse
{
Width = 50,
Height = 50,
Margin = new Thickness(3),
Stroke = new SolidColorBrush(Color.FromRgb((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255))),
StrokeThickness = 3
};
// Add to collection of ellipses
Ellipses.Add(ellipse);
};
BtnRemoveEllipse.Click += delegate
{
// Check, that Ellipses collection isn't null and empty,
// so we can remove something from it
if (Ellipses?.Count > 0)
Ellipses.Remove(Ellipses.Last()); // Removing last element
};
}
}
}
So at result you see, actually, "content of collection of Ellipses", without adding Ellipses directly to window. Binding makes WrapPanel to use collection of Ellipses as source of child elements, that should be in that WrapPanel (instead of original my answer, where we add Ellipse to Canvas as Children).
ORIGINAL answer.
Yes, you can. For example (based on your XAML):
XAML (empty window):
<Window x:Class="WPFApp.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:local="clr-namespace:WPFApp"
mc:Ignorable="d">
<!-- No even Grid here -->
</Window>
Code-behind (check comments also):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Setting Window properties (they not exists in XAML)
// XAML: <Window ... Title="Main Window" Height="450" Width="800">...
this.Title = "Main Window";
this.Height = 450;
this.Width = 800;
// Create main Grid and register some its name
// XAML: ...
var mainGrid = new System.Windows.Controls.Grid();
this.RegisterName("MainGrid", mainGrid);
// Add row and column definitions (as Canvas below needs, at least 4 rows and 3 columns)
for (int i = 0; i < 4; i++)
{
mainGrid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition { Height = new GridLength(50, GridUnitType.Star) });
if (i < 3) // Needn't 4th column
mainGrid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition { Width = new GridLength(50, GridUnitType.Star) });
}
// Create Canvas and register its name too
// XAML: ...
var canvas = new System.Windows.Controls.Canvas
{
// Just to be able see it at Window
Background = System.Windows.Media.Brushes.LightGray
};
this.RegisterName("GraphDisplayFrame", canvas);
canvas.SetValue(System.Windows.Controls.Grid.ColumnProperty, 1);
canvas.SetValue(System.Windows.Controls.Grid.RowProperty, 0);
canvas.SetValue(System.Windows.Controls.Grid.ColumnSpanProperty, 3);
canvas.SetValue(System.Windows.Controls.Grid.RowSpanProperty, 4);
// Create Ellipse (child canvas element)
// XAML: ...
var ellipse = new System.Windows.Shapes.Ellipse
{
Width = 50,
Height = 50,
Stroke = System.Windows.Media.Brushes.Black,
StrokeThickness = 2
};
ellipse.SetValue(System.Windows.Controls.Canvas.LeftProperty, 100D);
ellipse.SetValue(System.Windows.Controls.Canvas.TopProperty, 100D);
// Add child Ellipse to Canvas
canvas.Children.Add(ellipse);
// or you already can find Canvas by its name:
(this.FindName("GraphDisplayFrame") as System.Windows.Controls.Canvas).Children.Add(ellipse);
// Add Canvas to MainGrid. Find Grid by its registered name too
(this.FindName("MainGrid") as System.Windows.Controls.Grid).Children.Add(canvas);
// Set main Grid as window content
this.Content = mainGrid;
}
}
So, as you can see, XAML markuping is quite more compact, that code-behinded one.
I'v a progressbar and an image.
When Progress Bar value is 50, image loaded by 50%.
I tried to add image as the progressbar foreground, but it have green shade. So ugly.
How can I do this?
To run this sample, you need a snake image which you can get from http://res.freestockphotos.biz/pictures/16/16242-illustration-of-a-green-snake-pv.png. I have used this url directly, but your should download image first and then use it.
You need a control template for your ProgressBar because you want to show percentage status too.
Otherwise normal ProgressBar would do.
Code can be used as is :
<Window x:Class="WpfControlTemplates._32794074.Win32794074"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Win32794074" Height="600" Width="1000">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="397*"/>
<RowDefinition Height="173*"/>
</Grid.RowDefinitions>
<ProgressBar x:Name="PBarCustom" Width="958" Height="200" Maximum="958" Value="958" Foreground="#FFE6E61F" Margin="17,185,17,11.932" ValueChanged="PBarCustom_ValueChanged">
<ProgressBar.Background>
<ImageBrush ImageSource="http://res.freestockphotos.biz/pictures/16/16242-illustration-of-a-green-snake-pv.png"/>
</ProgressBar.Background>
<ProgressBar.Template>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}">
<Rectangle x:Name="Thumb" HorizontalAlignment="Left" Fill="#FFC5EA1F" Stroke="#FF0DB442" Width="{TemplateBinding Width}" />
<Ellipse Fill="#FF7DEEDE" Height="124" Stroke="#FF0DB442" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center" Opacity="0.3"/>
<Label x:Name="tbStatus" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontWeight="Bold" FontSize="75" Foreground="#FF21BD76" Content="0" />
</Grid>
</ControlTemplate>
</ProgressBar.Template>
</ProgressBar>
<Button x:Name="BtnLoadSnake" Content="Load Snake" HorizontalAlignment="Left" Margin="462,14.068,0,0" VerticalAlignment="Top" Width="75" Click="BtnLoadSnake_Click" Grid.Row="1"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WpfControlTemplates._32794074
{
/// <summary>
/// Interaction logic for Win32794074.xaml
/// </summary>
public partial class Win32794074 : Window
{
public Win32794074()
{
InitializeComponent();
}
DispatcherTimer timer;
private void BtnLoadSnake_Click(object sender, RoutedEventArgs e)
{
BtnLoadSnake.IsEnabled = false;
PBarCustom.Value = PBarCustom.Maximum;
Rectangle thumb = (Rectangle)PBarCustom.Template.FindName("Thumb", PBarCustom);
thumb.Width = PBarCustom.Value;
Label status = (Label)PBarCustom.Template.FindName("tbStatus", PBarCustom);
status.Content = ((int)(100 - ((100 * PBarCustom.Value) / PBarCustom.Maximum))).ToString();
Dispatcher disp = PBarCustom.Dispatcher;
EventHandler pBarCallbackHandler = new EventHandler(pBarCallback);
timer = new DispatcherTimer(TimeSpan.FromSeconds(0.5), DispatcherPriority.Normal, pBarCallback, disp);
}
private void pBarCallback(object sender, EventArgs e)
{
PBarCustom.Value -= 13;
Rectangle thumb = (Rectangle)PBarCustom.Template.FindName("Thumb", PBarCustom);
thumb.Width = PBarCustom.Value;
Label status = (Label)PBarCustom.Template.FindName("tbStatus", PBarCustom);
status.Content = ((int)(100 - ((100 * PBarCustom.Value) / PBarCustom.Maximum))).ToString();
if (PBarCustom.Value == 0)
timer.Stop();
}
private void PBarCustom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if(e.NewValue == 0)
BtnLoadSnake.IsEnabled = true;
}
}
}
i had similar scenario.
if you want the scroll bar to be just a rectangle,
easiest way to make it:
1- add an image to your window.
2- put a grid on it in such a way that the grid hides the image.
3- programatically change the width or height of the grid.
tell me if you needed an example code.
I have a Canvas and I added 20000 Line objects to it as below.
for (var i = 0; i < 20000; i++)
{
var l = new Line
{
X1 = 10,
Y1 = 10,
X2 = 10,
Y2 = 100,
Stroke = Brushes.White
};
canvas.Children.Add(l);
}
Now let's say I want to remove these lines from the Canvas. I do this like below:
canvas.Children.Clear();
But this doesn't Clear the memory and it is like the data is stuck there. So when I add another 20000 Line objects the memory just explodes after some time.
I know that Line has overhead and I shouldn't use it in the first place but my problem is now in another area. How to clear the canvas of 20000 lines and draw new ones without increasing the memory.
Are you actually sure they're not going? I've just put the following demo application together.
XAML
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Canvas Grid.Row="0" Name="canvas" Background="Black"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Add" Name="btnAdd" Click="btnAdd_Click" />
<Button Grid.Column="1" Content="Remove" Name="btnRemove" Click="btnRemove_Click"/>
</Grid>
</Grid>
</Window>
C#
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApplication7
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 20000; i++)
{
var l = new Line
{
X1 = 10,
Y1 = 10,
X2 = 10,
Y2 = 100,
Stroke = Brushes.White
};
canvas.Children.Add(l);
}
}
private void btnRemove_Click(object sender, RoutedEventArgs e)
{
canvas.Children.Clear();
}
}
}
And check the memory usage with Ants memory profiler (http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/).
After pressing the add button and adding the lines, this is the instance list for line.
you can clearly see the line instances in that one, then after pressing remove you can see the line instances have completely gone as well as the memory usage going down on the graph at the top.
I am wanting to create a small program that would let me select from a few images of interconnecting pieces to create what is essentially a path. That idea would be to have a starting piece with a button to where you could connect the next piece of the path. When clicked, this button would offer up a selection of the options that can be placed there and then the selected image would be placed and the button moved to the new end of the path.
My problem is that my c# experience is so far limited to static UI and manipulating text fields or entire new windows. I don't have a clue about where to start to make a UI in which buttons are moved and images are placed after the initial start of the program. I thought maybe using the grid control and some code to manipulate it might be the answer, but really don't know the commands to do such. I have been using WPF for my previous programs, and assume it would still be viable in this case.
Would anyone be able to point me in the right direction to figuring out how to dynamically control a section of the program's window in order to accomplish my goal? I am sorry for the semi-vague question but this is well out of my wheelhouse as a still very new hobbyist programmer.
Here's a quick, rather crude example, but it shows some basics of adding and positioning controls at runtime.
Things to note:
Add controls to a Canvas if you want to be able to position x,y explicitly.
I used UserControls for location items, and just added instances
If you're building a game, and if it's any significant size and complexity, don't use WPF. I've been there, done that, built a full multiplayer space arcade/action game on it. It's too slow for games.
Here's the code dump below.
//PathBuilding.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TravianResourceProd" x:Class="TravianResourceProd.PathBuilding"
Title="PathBuilding" Height="466.377" Width="621.509">
<Grid x:Name="drawingGrid" Background="#FFC2C2C2" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0*"/>
</Grid.ColumnDefinitions>
<Canvas Background="#FFB3B3B3" Margin="0,0,0,-0.377" x:Name="DrawCanvas" MouseMove="DrawCanvas_MouseMove" MouseUp="Grid_MouseUp">
<local:ActiveLocation x:Name="primarySegment" HorizontalAlignment="Left" VerticalAlignment="Top" Loaded="ActiveLocation_Loaded" Canvas.Left="67" Canvas.Top="98"/>
</Canvas>
</Grid>
</Window>
//PathBuilding.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace TravianResourceProd
{
/// <summary>
/// Interaction logic for PathBuilding.xaml
/// </summary>
public partial class PathBuilding : Window
{
public PathBuilding()
{
InitializeComponent();
_ConnectorLine = new Line();
_ConnectorLine.Stroke = new SolidColorBrush(Colors.DarkBlue);
_ConnectorLine.Visibility = System.Windows.Visibility.Hidden;
_locationSelector = new LocationOptions();
_locationSelector.Visibility = System.Windows.Visibility.Hidden;
DrawCanvas.Children.Add(_ConnectorLine);
DrawCanvas.Children.Add(_locationSelector);
}
private Line _ConnectorLine;
private bool _AddMode = false;
private LocationOptions _locationSelector;
private void ActiveLocation_Loaded(object sender, RoutedEventArgs e)
{
primarySegment.btnAddSegment.Click += (object sender1, RoutedEventArgs e1) =>
{
//show the type selector
_locationSelector.Visibility = System.Windows.Visibility.Visible;
var loc = _locationSelector.TransformToAncestor(drawingGrid)
.Transform(new Point(0, 0));
Canvas.SetLeft(_locationSelector, Mouse.GetPosition(DrawCanvas).X + 80);
Canvas.SetTop(_locationSelector, Mouse.GetPosition(DrawCanvas).Y - 50);
};
_locationSelector.btnTypeOne.Click += (object s, RoutedEventArgs e2) =>
{
_AddMode = true;
_ConnectorLine.Visibility = System.Windows.Visibility.Visible;
_locationSelector.Visibility = System.Windows.Visibility.Hidden;
};
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!_AddMode)
return;
_AddMode = false;
_ConnectorLine.Visibility = System.Windows.Visibility.Hidden;
//Add the one we picked
var oldLoc = new OldLocation();
Canvas.SetLeft(oldLoc, Canvas.GetLeft(primarySegment));
Canvas.SetTop(oldLoc, Canvas.GetTop(primarySegment));
DrawCanvas.Children.Add(oldLoc);
//Add a line connecting old to new
var newestLine = new Line();
newestLine.Visibility = System.Windows.Visibility.Visible;
newestLine.Stroke = new SolidColorBrush(Colors.Brown);
newestLine.X1 = _ConnectorLine.X1;
newestLine.Y1 = _ConnectorLine.Y1;
newestLine.X2 = _ConnectorLine.X2 + 40;
newestLine.Y2 = _ConnectorLine.Y2 + 50;
DrawCanvas.Children.Add(newestLine);
//Move the active/primary to the new location
Canvas.SetLeft(primarySegment, e.GetPosition(this).X);
Canvas.SetTop(primarySegment, e.GetPosition(this).Y);
}
private void DrawCanvas_MouseMove(object sender, MouseEventArgs e)
{
try
{//reposition the line going from active location to mouse
_ConnectorLine.X1 = Canvas.GetLeft(primarySegment) + 70;
_ConnectorLine.Y1 = Canvas.GetTop(primarySegment) + 50;
_ConnectorLine.X2 = e.GetPosition(this).X - 5;
_ConnectorLine.Y2 = e.GetPosition(this).Y - 5;
}
catch (Exception)
{
}
}
}
}
//LocationOptions.xaml
<UserControl x:Class="TravianResourceProd.LocationOptions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="109.359" Width="117.057">
<Grid Margin="0,0,-0.17,0.094">
<Button x:Name="btnTypeOne" Content="Type One" HorizontalAlignment="Left" VerticalAlignment="Top" Width="117" Height="33" Margin="0,0,-0.17,0" />
<Button x:Name="btnTypeTwo" Content="Type Two" HorizontalAlignment="Left" VerticalAlignment="Top" Width="117" Margin="0,38,-0.17,0" Height="33" />
</Grid>
</UserControl>
//OldLocation.xaml
<UserControl x:Class="TravianResourceProd.OldLocation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="80" Width="80">
<Grid>
<Ellipse Stroke="#FF686868" StrokeThickness="8">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF373737" Offset="1"/>
<GradientStop Color="#FF929292"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</UserControl>
//ActiveLocation.xaml
<UserControl x:Class="TravianResourceProd.ActiveLocation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="80" Width="80">
<Grid>
<Ellipse Stroke="#FF1A9000" StrokeThickness="6">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF62745E" Offset="1"/>
<GradientStop Color="#FF929292"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Button x:Name="btnAddSegment" Content="" HorizontalAlignment="Left" VerticalAlignment="Top" Width="20" Height="22" FontSize="30" Margin="60,30,-0.302,0"/>
</Grid>
</UserControl>
I have also posted this on MSDN forums - i hope its not a problem.
I am basically trying to create a WPF based video player which allows you to seek within media. I'm trying to implement it using MediaTimeline (i know i can change the Position property, but i had other issues which i'll post in a separate question). XAML and code-behind are below.
Thanks for looking...
MainWindow.xaml
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<MediaElement x:Name="mediaElement" Width="320" Height="240" Margin="4" LoadedBehavior="Manual"/>
<Slider x:Name="slider" Grid.Row="1" Margin="4"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace WpfApplication5
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var tl = new MediaTimeline(new Uri(#"c:\temp\!numa.wmv"));
mediaElement.Clock = tl.CreateClock(true) as MediaClock;
mediaElement.MediaOpened += (o, e) =>
{
slider.Maximum = mediaElement.NaturalDuration.TimeSpan.Seconds;
mediaElement.Clock.Controller.Pause();
};
slider.ValueChanged += (o, e) =>
{
mediaElement.Clock.Controller.Seek(TimeSpan.FromSeconds(slider.Value), TimeSeekOrigin.BeginTime);
};
}
}
}
You need to set ScrubbingEnabled="True" on MediaElement to have it update during seeks.
the MediaOpened event should actually be setting the maximum value to .TotalSeconds, and you should also set ScrubbingEnabled to True as jesperll pointed out.