I am new at MVVM. Currently all my code is written in .cs file which linked to XAML. I want switch to MVVM but experiencing difficulties. I will try to explain why:
I have many different Chart controls and input data specified in .cs file in the way that I am accessing Chart object directly and using it's properties programaticaly to add points for my chart.
Example:
foreach (var group in qcv.Groups)
{
AreaSeries areaSeries = new AreaSeries();
areaSeries.CombineMode = Telerik.Charting.ChartSeriesCombineMode.Stack;
areaSeries.ValueBinding = new PropertyNameDataPointBinding("Rev");
areaSeries.CategoryBinding = new PropertyNameDataPointBinding("Date");
areaSeries.ItemsSource = group as IEnumerable;
RadChart1.Series.Add(areaSeries);
}
But as long as I switch to MVVM RadChart1 objects gets inaccessible in ViewModel file. How can I make it visible in ViewModel class or maybe you can suggest better approach how I can get that object and provide input for my chart without changing my code behind?
My XAML File:
<UserControl x:Class="FrontEnd.RevenueChart"
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"
xmlns:local="clr-namespace:FrontEnd"
mc:Ignorable="d" xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" HorizontalAlignment="Stretch" >
<UserControl.DataContext>
<local:RevenueChartViewModel/>
</UserControl.DataContext>
<Grid>
<telerik:RadCartesianChart HorizontalAlignment="Stretch" x:Name="RadChart1" Palette="Metro" Zoom="10,1">
<telerik:RadCartesianChart.HorizontalAxis>
<telerik:CategoricalAxis/>
</telerik:RadCartesianChart.HorizontalAxis>
<telerik:RadCartesianChart.VerticalAxis>
<telerik:LinearAxis/>
</telerik:RadCartesianChart.VerticalAxis>
<telerik:RadCartesianChart.Behaviors>
<telerik:ChartPanAndZoomBehavior ZoomMode="Both">
</telerik:ChartPanAndZoomBehavior>
</telerik:RadCartesianChart.Behaviors>
</telerik:RadCartesianChart>
</Grid>
</UserControl>
Accessing view controls from the viewmodel is a big no-no in MVVM land. You have to invert your thinking: instead of adding stuff to Series, bind Series to stuff. Use SeriesMappings to get the chart control to convert your groups to series. Here's a bit of off-the-cuff code to get you started:
<telerik:RadCartesianChart HorizontalAlignment="Stretch"
Palette="Metro" Zoom="10,1"
Series="{Binding Groups}"><!-- <=== this is the important part -->
<telerik:RadChart.SeriesMappings>
<telerikCharting:SeriesMapping LegendLabel="Product Sales">
<telerikCharting:SeriesMapping.SeriesDefinition>
<telerikCharting:AreaSeriesDefinition/>
</telerikCharting:SeriesMapping.SeriesDefinition>
<telerikCharting:SeriesMapping.ItemMappings>
<telerikCharting:ItemMapping DataPointMember="XCategory" FieldName="Date"/>
<telerikCharting:ItemMapping DataPointMember="YValue" FieldName="Rev"/>
</telerikCharting:SeriesMapping.ItemMappings>
</telerikCharting:SeriesMapping>
</telerik:RadChart.SeriesMappings>
...
Related
I am Trying to use the new RadSyntaxEditor from Telerik by following this guide.
This is the code I created:
private RadSyntaxEditor _syntaxEditor;
public RadSyntaxEditor SyntaxEditor
{
get => _syntaxEditor;
set
{
if (Equals(value, _syntaxEditor)) return;
_syntaxEditor = value;
OnPropertyChanged();
}
}
public CodeEditorViewModel()
{
SyntaxEditor = new RadSyntaxEditor();
}
public void Test()
{
using (StreamReader reader = new StreamReader("../../ViewModels/ShellViewModel.cs", Encoding.UTF8))
{
SyntaxEditor.Document = new TextDocument(reader);
}
var cSharpTagger = new CSharpTagger(SyntaxEditor);
SyntaxEditor.TaggersRegistry.RegisterTagger(cSharpTagger);
}
my xaml file:
<UserControl x:Class="CodeEditorControl.Views.CodeEditorView"
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"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="Test" Grid.Row="0">Test</Button>
<telerik:RadSyntaxEditor x:Name="SyntaxEditor" Grid.Row="1"/>
</Grid>
</UserControl>
The control is showing without a problem and is responding to input etc.
But neihter does the document load, nor is there any syntax highlighting.
The Reader loads correct and ReadToEnd() outputs the correct text (ShellViewModel is just a standard cs file with 36 lines).
I am using caliburn.micro and the MVVM design.
Edit: I set up a project with the same template but using code behind instead of binding. This works as intended. So the problem lies within the binding from caliburn.micro and telerik.
Any help is appreciated.
I've noticed that the property in CodeEditorViewModel is of type RadSyntaxEditor and the corresponding UI element is also RadSytanxEditor. Note that this produces a binding error in the Output pane of Visual Studio. I think that the Caliburn.Micro binding engine cannot create this type of relation and currently there are two separate instances of RadSyntaxEditor. The one defined in XAML and the other one defined in the view model. The document is loaded to the one defined in code, but because it is never used in the UI, there is nothing in the application.
To resolve this you can research the Caliburn.Micro framework and more specifically, how to use the naming conventions to data bind the model property to a corresponding property of the UI element. I think the current binding (via the convention) defaults to the Visibility property of RadSyntaxEditor.
Or you can simply use an explicit data binding like this:
<Button x:Name="Test" Grid.Row="0">Test</Button>
<ContentControl Content="{Binding SyntaxEditor}" Grid.Row="1"/>
Note that I've replaced the RadSyntaxEditor control with a ContentControl.
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.
I made a WPF control in a library project and would like to extend it with a new one.
<UserControl x:Class="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI.EmbeddingUserControl"
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>
</Grid>
</UserControl>
I have tried to extend it like this:
<src:EmbeddingUserControl x:Class="Interaxalab.Desktop.Modules.PrototipoCable.CustomViews.InteractionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI"
Name="InteractionWorksheetView" Height="321.613" Width="471.396"
>
<Grid>
<WindowsFormsHost x:Name="windowsFormsHost1" HorizontalAlignment="Left" Height="284" VerticalAlignment="Top" Width="471"/>
</Grid>
</src:EmbeddingUserControl>
However, I get an error message saying that the name "EmbeddingUserControl" does not exist in namespace "Genesyslab.Desktop.Modules.ExtensionUtils85.GUI".
The name clearly does exist, since the xaml.cs can find it, but for some reason the xaml cannot.
Any help would be appreciated.
Long story short - you cannot inherit control with xaml by another control with xaml (and does it makes sense even to do so?). In your case, EmbeddingUserControl does not contain any visual tree elements (just empty grid), so you can just do:
public class EmbeddingUserControl : UserControl {
// some methods, properties of your control
}
Then you can inherit exactly like you do already in your question (don't forget to inherit from EmbeddingUserControl both in xaml file and in code-behind file).
You can also inherit from user control with xaml, if your inherited control does not have xaml itself (so you can add\override logic).
If you need to inherit some visual stuctures - you have to switch from inheritance to composition. That is: your base control provides some placeholders where other controls may be placed, or even better allows to provide templates to format data items (like for example ItemsControl and ItemTemplate property). Then you just fill those placeholders with other controls if necessary.
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.
My WPF window should be able to load in different controls in same spot on the window; which should be frames to fulfill that task.
Hence i'm trying to make a frame load different pages by editing a databound string containing the Frames source. And I have managed to do that, however at the moment I have no idea how to share the frames data to the windows viewmodel hosting the frame.
I'm using MVVM and I thougth that if I could also databind a "viewmodel" to the frames datacontext, I could then both choose which page to load and which datacontext the page should use, all from the host window, therefore having access to it.
Below is my xaml.
<Window x:Class="View.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window" Height="300" Width="300">
<Grid>
<Frame NavigationUIVisibility="Hidden" DataContext="{Binding WindowClass.DataContext}" Source="{Binding WindowClass.FrameURI}"/>
</Grid>
However, if I now assign the pages datacontext through this binding, instead of in the code behind, nothing gets loaded. Now I basically end up with a blank frame.
Why?
You can use Window.Resources to bind to your DataContext, then Bind to the FrameURI (You'll need to fix the appropriate namespace instead of my custom xmlns:WindowClass):
<Window x:Class="View.Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WindowClass="clr-namespace:WindowClass"
Title="Window" Height="300" Width="300">
<Window.Resources>
<WindowClass:MyViewModelName/>
</Window.Resources>
<Grid>
<Frame NavigationUIVisibility="Hidden" DataContext={Binding} Source="{Binding FrameURI}"/>
</Grid>
You can find a very basic tutorial here