WPF - canvas declaration xaml - c#

Is it possible to declare a canvas in xaml without it being local to the MainWindow.xaml.cs page?
I have created a class called ImageBoard, but I need this class to have the xaml canvas held locally within it - and not within Mainwindow.xaml.cs.
What I want is the ImageBoard to act as the manager of this canvas.
The only other way I can imagine doing this is to have the ImageBoard derive from Canvas itself, so that it is the canvas. Then I could place this into the MainWindow.
At the moment I have this.
Xaml:
<Canvas HorizontalAlignment="Center" Height="370" VerticalAlignment="Center" Width="688"
x:Name="canvas1" Background="#f6f6f6"/>
But it can only be called from the MainWindow, as it is part of the MainWindow. Because of this I have to pass it to the ImageBoard to perform various Methods.
What I think would be best is this
Public class ImageBoard : Canvas
which would eliminate the need for passing the canvas.
I did try to declare a reference to the canvas, via the ImageBoard's constructor, but I think this would be bad practice; as I am, essentially, using the ImageBoard for all drawing purposes anyway. Why keep a canvas when I can turn the ImageBoard into a canvas, right?

You could build a ImageBoard UserControl:
XAML:
<UserControl x:Class="WpfMagic.ImageBoard"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Canvas x:Name="canvas" Background="Transparent" MouseLeftButtonUp="Canvas_MouseLeftButtonUp"></Canvas>
</Grid>
</UserControl>
Code behind:
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfMagic
{
public partial class ImageBoard : UserControl
{
public ImageBoard()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TextBlock tb = new TextBlock { Text = "*", FontSize = 20 };
tb.SetValue(Canvas.TopProperty, e.GetPosition(canvas).Y);
tb.SetValue(Canvas.LeftProperty, e.GetPosition(canvas).X);
canvas.Children.Add(tb);
}
}
}
And from you main page you can do:
<Window x:Class="WpfMagic.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMagic"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:ImageBoard></local:ImageBoard>
</Grid>
</Window>
This way you never directly manipulate the Canvas, it is abstracted away from the code using the ImageBoard, this is encapsulation.

Related

WPF: Problem with display xaml view inherits from UserControl

Hello i have this Viw in XAML
<local:JedenViewBase x:Class="Firma.View.FakturaView"
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:Firma.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</UserControl.Resources>
<Grid>
.....
</Grid>
</local:JedenViewBase>
And that is class this view
namespace Firma.View
{
public partial class FakturaView : JedenViewBase
{
public FakturaView()
{
InitializeComponent();
}
}
}
And that is JedenViewBase class
namespace Firma.View
{
public class JedenViewBase : UserControl
{
static JedenViewBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(JedenViewBase), new FrameworkPropertyMetadata(typeof(JedenViewBase)));
}
}
}
I have problem because view in XAML dont display, i dont know why? JedenViewBase class inherits from UserControl. When i UserControl in view everything works. What i should do?
<UserControl x:Class="Firma.View.FakturaView"
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:Firma.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</UserControl.Resources>
<Grid>
...
</Grid>
</UserControl>
View FakutraView when i use UserControl
I try rebuild app etc. and i still have problem
You've created a custom control.
That's unlikely to be a good idea and this should probably just be a user control.
The reason you get no view is this.
static JedenViewBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(JedenViewBase), new FrameworkPropertyMetadata(typeof(JedenViewBase)));
}
I recommend you remove that. Change
<local:JedenViewBase
To user control.
Make this just a user control.
Alternatively. Read up on custom controls. Put your ui definition in generic xaml.
I also wonder why this has it's own resources. They will be in memory for each instance. If whatever is in that resource dictionary is unique to this control maybe that's not a bad idea. in which case the naming seems strange.

Cannot display binding on usercontrol?

I'm not able to display a property value on the usercontrol.
I set up the datacontext in this way:
public MainController cm;
public static MainWindow AppWindow;
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
cm = new MainController();
DataContext = cm;
AppWindow = this;
}
}
inside MainController I've all the controller with all the properties like this:
public class MainController: MainControllerVM
{
private ClubController _clubController = new ClubController();
public ClubController ClubController
{
get { return _clubController ; }
}
}
Now I've splitted my user interface in different controls to have more xaml organization. I need to access to the main datacontext that's cm from all user controls, I tried in this way:
public partial class Club : UserControl
{
public Club ()
{
InitializeComponent();
DataContext = MainWindow.AppWindow.cm;
}
but I get:
NullReferenceException
on AppWindow. My main problem's that I can't get to display the value of the property on a label available on the user control:
<Label Content="{Binding ClubController.Club.Name}" />
this binding working in the main window but not working on usercontrol, why??
Suppose you have a window like this:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
And you set the DataContext in the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Now remember that the DataContext is an inheritable dependency property, i.e. it flows down. (In general, dependency properties are not inheritable by default, unless you explicitly state it)
So, you set the DataContext once on the root of the logical tree (the window) and all of its children will "see" it. (the UniformGrid and the custom controls in our case)
Yes, that means you can directly bind to the view model in your user control's XAML:
<UserControl x:Class="Example.MyUserControlA"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding PropertyFromMainViewModel}"/>
</Grid>
</UserControl>
Now, this approach works well, until your control gets so complicated that it needs to have its own ViewModel and DataContext reespectively.
Usually this happens when the control is not a passive, but maintains a state (validates input, button state, etc.)
1.Declare all properties that you want to bind to the main view model as dependency properties and pay attention to the default value you specify.
2.Locate the main panel of your UserControl and name it, for example "LayoutRoot":
<UserControl x:Class="Example.MyUserControlA"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding MyDependencyProperty}"/>
</Grid>
</UserControl>
3.Now, you set the DataContext on the LayoutRoot
public MyUserControlA()
{
InitializeComponent();
LayoutRoot.DataContext = new MyUserControlViewModel();
}
4.You bind to the main view model in this way
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA MyDependencyProperty="{Binding MainViewModelProperty}"/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
The other way around is to bind using RelativeSource, but this would break the encapsulation and reusability of your UserControl.
WPF has a steep learning curve, I hope my tips were helpful...

Loading a grid in xaml code from a file, then making it active in main code

I am making a video game, where each grid serves as game room. Each grid\room has a large number of objects like things that can be used, images and sound files. Until now, they all were stored in one large file. But now, I was told that this approach wastes a lot of resources.
My plan is, to store xaml code of every such grid as a separate file, then load relevant file at run time with usual code.
For now xaml looks about like this:
<Window
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"
mc:Ignorable="d" x:Name="wdwMain" x:Class="RealityIncognita.MainWindow"
Height="900" Width="1600" ResizeMode="NoResize" WindowState="Maximized"
Cursor="Cross" WindowStyle="None" Loaded="wdwMain_Loaded">
<Viewbox Stretch="Fill">
<Grid x:Name="areaContainer" HorizontalAlignment="Left" Height="900"
VerticalAlignment="Top" Width="1600">
<Grid x:Name="areaMain">
<Grid.Background>
<ImageBrush
ImageSource="Resources/Images/Interface/main_interface.jpg"/>
</Grid.Background>
<Grid x:Name="areaShowers" HorizontalAlignment="Left" Height="700"
Margin="1653,790,-1561,-590" VerticalAlignment="Top" Width="1508"
IsVisibleChanged="areaShowers_IsVisibleChanged">
Here's example - areaShowers is a grid for relevant room. Until now, it was stored in the main file, like all other grids, and when I needed, I just altered its Margin to put it upon "areaMain" - also a grid.
Now though, I want to put each room into a file, then load it when I need it, and remove it from memory when I don't.
For example I'll create an empty grid "areaGeneric" and add it and it alone to original xaml.
So, I want something like this. Can't provide any earlier attempt, because I don't really know how it can be done.
Grid new_grid = new Grid;
new_grid = load from file (areaRoom.xaml); (file is among the project's
resources)
areaGeneric = new_grid;
Can I load a grid xaml at run-time, then switch grids in main code?
Thank you,
Evgenie
A really simple solution would just be to house each of your rooms inside a user control and then place the control into your container grid when you need to change rooms. Here's the rough idea:
public partial class MainWindow : Window
{
UserControl _currentRoom;
public MainWindow()
{
InitializeComponent();
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
this.areaContainer.Children.Clear();
_currentRoom = null;
if (e.LeftButton == MouseButtonState.Pressed)
_currentRoom = new Room1();
if(e.RightButton == MouseButtonState.Pressed)
_currentRoom = new Room2();
this.areaContainer.Children.Add(_currentRoom);
base.OnPreviewMouseDown(e);
}
}
A "Room":
<UserControl x:Class="Test.Room1"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Button Height="100" Width="100">
Hello world!
</Button>
</UserControl>
As for cleaning up your resources - once the user control is removed from the visual tree (assuming you aren't holding a reference) the GC should dispose of the user control. If you wanted to clear your resources more quickly you could implement a dispose method on your rooms and then call that before you change areas.

Why does Source for ContextMenuOpening behave differently for Canvas and UserControl?

I have a simple window:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:WpfApplication1"
Title="MainWindow" Height="435" Width="613">
<StackPanel>
<Canvas Name="canvas">
<self:Red />
</Canvas>
<UserControl Name="uc">
<self:Blue />
</UserControl>
</StackPanel>
</Window>
Redand Blueare very simple UserControls:
<UserControl x:Class="WpfApplication1.Blue"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Rectangle Fill="Blue" Width="100" Height="100" />
</Grid>
</UserControl>
I have created some ContextMenus:
public MainWindow()
{
InitializeComponent();
canvas.ContextMenu = new ContextMenu();
canvas.ContextMenuOpening += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine(e.Source.GetType());
};
uc.ContextMenu = new ContextMenu();
uc.ContextMenuOpening += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine(e.Source.GetType());
};
}
If I open the context menu on the Canvas, the Source is Red, but if I open it on the UserControl, Source is UserControl.
Any idea why?
I found this on MSDN:
ContextMenu itself is a FrameworkElement derived class, but this event will not be raised from the context menu being opened as a source. The event is raised from the element that "owns" the context menu as a property...
If I understand it correctly Source should be Canvas in the first case, but it isn't.
This behavior is covered fairly well in the MSDN documentation for the RoutedEventArgs.OriginalSource property:
Source adjustment by various elements and content models varies from class to class. Each class that adjusts event sources attempts to anticipate which source is the most useful to report for most input scenarios and the scenarios for which the class is intended, and then sets that source as the Source. If this source is not the one that has relevance to your handling of the event, try checking OriginalSource instead to see if it reports a different source that is more suitable.
Which is exactly what the UserControl class does, it patches the Source property in its AdjustBranchSource() method.
So, as hinted by the quoted text, you are perhaps looking for the OriginalSource property to make the code behave similarly, you'll get a reference to the Rectangle in both cases.

How can i link a canvas on a grid, to the mainwindow.xaml?

I have a canvas in a grid, i want to keep my canvas on that grid, because its the first window, that opens in my program.
In my MainWindow.xaml, i have a ContentPage, that always changes its content, the startup content is the authenticationPage. In this page i have a Canvas that shows my skeletal tracking, and is used for making a gesture. This gestureCanvas is on my authenticationPage. The code behind this gestureCanvas is on my MainWindow.xaml.cs.
I need to link the gestureCanvas with my MainWindow.xaml.cs, because the code is behind MainWindow, and it's going to be used there, because it's an Kinect application.
How to link these ?
partial class MainWindow
{
void LoadCircleGestureDetector()
{
using (Stream recordStream = File.Open(circleKBPath, FileMode.OpenOrCreate))
{
circleGestureRecognizer.TraceTo(gesturesCanvas, Colors.Red);
}
}
}
This is my authenticationPage
<UserControl
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:smartHome2011"
xmlns:MyUserControl="clr-namespace:MyUserControl;assembly=MyUserControl"
mc:Ignorable="d"
x:Class="smartHome2011.AuthenticationPage"
x:Name="UserControl"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid x:Name="kinectGrid" HorizontalAlignment="Left">
<Viewbox Margin="204,220,430,220">
<Grid ClipToBounds="True" Margin="204,220,430,220">
**<Canvas x:Name="gesturesCanvas" />**
<Canvas x:Name="kinectCanvas"></Canvas>
</Grid>
</Viewbox>
</Grid>
</Grid>
at your code behind MainWindow you can try following
var gesturesCanvas = YourContentPage.FindName("gesturesCanvas") as Canvas;
if (gesturesCanvas != null) {
// do something
}
hope this helps

Categories

Resources