please how could I change the position of the elements in the UI, or choose a different design when the application loads?
It could be done using User Controls for each design, but the bad thing about this solution is that the same code will be repeated and I do not want that.
Please what would be the best practices to achieve this, it should be noted that the controls must have a name to use it in the code.
Thanks in advance.
Summary: This is what I want to achieve
In WPF the layout is all XAML. XAML can be stored in a resource dictionary. So once you determine what layout you want you can load the correct resource dictionary. Basically this pattern is exactly like people loading themes to change the colors of a UI etc.
I once had a situation similar to yours.
In your case, you should design your grid first as follows,
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LeftButtonColWidth}"/>
<ColumnDefinition Width="{Binding RightMainPanelColWidth}"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height = "32"/>
<RowDefinition Height = "*"/>
<RowDefinition Height = "{Binding ightMainPanelBottomButtonRowHeight}"/>
</Grid.RowDefinitions>
Then add properties "LeftButtonColWidth", "RightMainPanelColWidth" and "RightMainPanelBottomButtonRowHeight" in viewmodel to control your layout based on some setting specified by end-users somewhere.
The above code is just for main container.
You also need a container grid for Buttons which should be designed as the main container grid using Binding property. In button container, you need bind Grid.Row and Grid.Column to properties "ButtonContainerRow" and "ButtonContainerCol" in ViewModel, they will be changed based on the some specific setting, when the app starts.
This is my solution. There must be other better solutions.
I hope someone can give me a solution only using xmal. That would be the perfect one.
Related
I am aware that I can use Grid.Rowdefinitions to define the number of rows and their properties on a WPF Grid control.
However is there a way to set the grid to automatically grow/add rows as controls are added, without having to explicitly state it?
However is there a way to set the grid to automatically grow/add rows as controls are added, without having to explicitly state it?
No, there isn't. Depending on your requirements, you probably want to replace the Grid with another Panel like for example a StackPanel or a UniformGrid with a single column:
<UniformGrid x:Name="grid" Columns="1" />
Then you don't need to care about setting any Grid.Row property.
You can do that in your code behind.
Define following in the .xaml of your window:
<Grid x:Name="YourGrid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
</Grid>
Now use a loop to create as many rows as you need:
foreach(Control control in controlls)
{
YourGrid.RowDefinitions.Add(new RowDefinition());
YourGrid.Children.Add(control);
Grid.SetRow(control , YourGrid.RowDefinitions.Count - 1);
}
If you have a lot of controls this could help you. It will add the control automatically into the created row.
If you don't want to add the controls and rows in the code behind, you will have to add the rows manually. As far as I know there is no way to automate it.
I recently came across MVVMCROSS which I think is a very good idea to solve cross platform issues. So I was tempted to port over an application I was working on and my entire libraries to make them more portable. Now I am trying ti bind my viewmodels to the views and I am facing several issues and questions...
I have seen the examples of how navigation works and how to start the first view which works fine. However, for my application I cannot use this.. My main view consists of a menu bar at the top of the window from where views can be called. The main window contains this :
<Grid Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu Name="MainMenu"
Height="25"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Grid.Row="0">
<MenuItem Header="Tools" Width="Auto">
<MenuItem Header="Solar estimator" HorizontalAlignment="Left" Width="Auto" Click="Menu_Click"/>
</MenuItem>
</Menu>
<DockPanel Grid.Row="1" Name="DockPanel" Height="Auto" Width="Auto">
</DockPanel>
So what I wish to do is that whenever I call a new view from the menu, I'll attach it to the dockpanel.
The function Menu_Click has this code :
EnergyNeedsEstimatorView estimatorView = new EnergyNeedsEstimatorView();
double estimatorViewHeight = estimatorView.Height;
double estimatorViewWidth = estimatorView.Width;
estimatorView.SetValue(UserControl.WidthProperty, double.NaN);
estimatorView.SetValue(UserControl.HeightProperty, double.NaN);
DockPanel.Children.Add(estimatorView);
DockPanel.Height = estimatorView.Height;
DockPanel.Width = estimatorView.Width;
this.Height = estimatorViewHeight + MainMenu.Height + SystemParameters.WindowCaptionHeight + SystemParameters.ResizeFrameHorizontalBorderHeight;
this.Width = estimatorViewWidth + SystemParameters.ResizeFrameVerticalBorderWidth + 10;
So view is displayed fine however, since I explicitly removed the IMvxAppStart part from my core library and removed the IMvxAppStart Resolve from DoSetup in my Desktop library, the link between the viewmodel and the view is no longer made. I suppose I could do like before and specify to my view that it's DataContext is the viewmodel and it would work like I did before using MVVMCross but I feel this is not the best approach.
So you probably are wondering, why not just add the menu bar to every view instead of trying to attach my views to a dockpanel? Well, I do not want to change my menu bars everywhere when I need to make a change. So I am trying to have a generic main window from where to display all views. Right now I am stuck and trying to figure out how I can do this with MVVMCross..
Any ideas?
If you ask the container to create you a View, then it automatically tries to Load a ViewModel for you - see the code in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Wpf/Views/MvxWpfViewsContainer.cs
So you could replace your new with a call to
car view = Mvx.Resolve<IMvxWpfViewsContainer>().CreateView(MvxViewModelRequest<MyViewModel>.GetDefaultRequest()):
However, normally in mvvmcross ViewModels and Applications don't talk directly to the Container - instead, they talk to a Presenter and it may be better to modify your code to use a presenter instead. Within this flow your menu would
- send the Presenter ViewModelRequest objects
- the Presenter then asks the Container for a View
- the Container loads the View and provides it with info on it's ViewModel (how this is done depends on the platform and on the View).
- the Presenter can then actually show the View on the display (normally a window) somehow.
In other frameworks, you may hear the term navigation service used instead of what Mvx calls Presenter
Also asked at http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/f765d4c9-1719-4757-b467-2492d87bb4ab
All,
I have a slider that performs a scale-transform in my WPF App. Now I'm running into an issue where I have an element inside of a scrollviewer inside of a row that has Height="*" and another element in a subsequent row that has Height="Auto".
The goal is that we don't know the row heights until run-time (or even how big the elements will be), but that we will have both elements displayed on the screen, with the first element taking up as much space as it can and the bottom element always being visible, taking up whatever space it needs.
I have the following problem/solution statement (which I think best describes my issue) and, as you can see, I'm stuck at what to do when I want to achieve this goal and still allow access to all UI elements should the zoom be large enough to push one of the elements off the screen (hopefully that made sense).
Problem statement:
ScaleTransform is causing top DG to disappear where RowDefinition Height="*"
Solution:
Set Minimum height on RowDefinition to prevent DG from disappearing.
Problem statement:
Setting Minimum height on RowDefinitoin causes lower rows to dissapear.
Solution:
Add scrollviewer encapsulating grid.
Problem statement:
Since top DG is in RowDefinition Height="*", if there is a lot of data in top DG, bottom DGs cannot be seen without scrolling.
Solution statement:
???
Here's my current code:
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="120" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox Grid.Row="0">
<DataGrid ItemsSource="{Binding Path=SomePathThatCouldHaveLotsOfData}">
<!--Note that DataGrid implements its own scrollviewer as long as it's not surrounded by another scrollviewer-->
<!--...-->
</DataGrid>
</GroupBox>
<GroupBox Grid.Row="1">
<!--...-->
</GroupBox>
Hopefully that all made sense what my issue is and what I'm trying to do. Any ideas how I can get this done (hopefully in a clean way). I'm using MVVM if it helps.
EDIT: I should point out that I also won't know any max-height until run-time (and max-height is probably irrelevant anyway since we'll be using scale-transform).
All,
I managed to acheive my goal, but it was a bit of an ugly hack.
Basically, I had to trap the Loaded and SizeChanged event of my UserControl/Page/Window and use these events to set the ScrollViewer.ViewPortHeight value to a property in my VM. Then, it was simply a matter of subtracting a known value from this to use as my Max value. Then, it was simply a matter of multiplying the ViewPortHeight by the percentage that I wanted each UI element to take up on the screen and bind the UIs element to that.
Hopefully this will help someone else having the same business requirement.
I am making kind of WPF Designer. I want to find out ColumnDefinition i have clicked on to delete it from grid control. I will take care of those children who "are in that ColumnDefinition".
Can i get it from sender argument of click event handler?
Now im checking if e.GetPosition is in range of ColumnDefinition.ActualWidth but i wonder if there is more beautiful solution.
From within your click event handler:
int columnIndex = Grid.GetColumn((UIElement)sender);
where sender if a direct grid's child.
Why do you need to capture a click on ColumnDefinition anyway? Is virtual, it does not have any actual body, it is only a hint for Grid on how you want to layout its content.
So you have to set handlers on content objects, not on ColumnDefinition.
If you really need to capture a click on the whole surface of a grid cell, you may try to place a white (or other color the same as background) Reactangle inside it and capture a click on it.
Some clarification on how WPF Grid works.
When you add some controls to the Grid, they all become its children.
<Grid>
<Button/>
<TextBox/>
<Label/>
</Grid>
And they all will be displayed not regarding how you have configured Column or RowDefinitions.
Column and RowDefinitions only tell Grid how you want to aling all the existing elements inside it, but they are not containers, they don't hold elements inside.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button/><!-- this is identical to Grid.Column="0"-->
<TextBox Grid.Column="1"/>
<Label Grid.Column="2"/>
</Grid>
In this example we have created three ColumnDefinitions, even from the grid XAML you can see, that controls are not inside definitions. They are used just like ruler guides to align content.
Then you set attached properties on the elements to tell the grid where you want to put your elements.
When grid begins layout, it will see, that there are three elements, and three ColumnDefinitions, and will try to positions elements as ColumnDefinitions says.
But if you remove or change ColumnDefinitions in the runtime, grid will just realign controls in a new way.
If you want to hide some elements, you have to hide them, not ColumnDefinition.
Is it possible and a good idea to have user control (public MyControl: UserControl) which supports both ControlTemplates and existing content? I have understood that ControlTemplates should only be used when you inherit from Control (public MyControl: Control), but I found out that you can use them with UserControl too if your UserControl.xaml is empty.
Imagine I have control which has two rectangles side by side like the following:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid ShowGridLines="true" Height="100" Width="100">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Name="left" Grid.Column="0" Height="90" Fill="LightBlue"/>
<Rectangle Name="right" Grid.Column="1" Height="100" Fill="LightGreen"/>
</Grid>
</Page>
I would like the user of the control be able to replace those rectangles with whatever FrameworkElements he wants to use. So I need a ControlTemplate.
But in 99% of the cases user of the control is happy with the existing functionality so I would like him to be able to say:
Code behind:
mycontrol.Left.Fill = ....
XAML:
<mycontrol>
<mycontrol.Left.Fill = "Red"/>
</mycontrol>
That doesn't seem to be possible since if I support control templates I really don't have any UI elements or xaml. I only have the code behind file. I guess I could have a DependencyProperty Left but as long as I don't have some kind of container which would hold the content that would't do much good. I would have to create the grid in code behind file. Doesn't seem like a good idea.
And finally I would like to be able to use generics so the user can specify the type of the parts:
MyControl mycontrol<TLeft, TRight> = new MyControl<Rectangle, Button>();
This would help in code behind because of the type safety (no need to cast FrameworkElement into correct type). Unfortunately I don't think generics are really supported on the XAML side.
Is there any solution to this problem or is it really "Inherit from Control in order to support ControlTemplates but lose the easy usability of the control. Inherit from UserControl in order to support easy usability but lose the ControlTemplate support"?
Add a dependency property to the control:
public static DependencyProperty LeftFillProperty = DependencyProperty.
Register("LeftFill", typeof(Brush), typeof(MyControl));
public Brush LeftFill
{
get { return (Brush)GetValue(LeftFillProperty); }
set { SetValue(LeftFillProperty,value); }
}
Then in the default control template use:
<Rectangle Name="left" Grid.Column="0" Height="90" Fill="{TemplateBinding LeftFill}"/>
This will left you use (C#)
ctrl.LeftFill = Brushes.Red;
or (XAML)
<c:MyControl LeftFill="Red"/>
when you use the default template and when someone writes a new control template it's their responsibility to decide what to do with the LeftFill property (or to ignore it completely).
BTW, you should consider changing the names from "left" and "right" to something else ("MasterPanel" and "DetailPanel", "FoldersArea" and "FilesArea", whatever the logical usage in your control is), this will solve the issue of someone replacing the default template with a template that displays the same data in a different layout (top and bottom instead of left and right for example).