How to remove child from UserControl? - c#

It's maybe stupid question but I just cant figure out how to do this. I have a UserControl named "ReportUserControl" and inside of it I have a Grid named "ReportGrid" . I want to remove ReportGrid from ReportUserControl children. I tried this:
ReportUserControl control = new ReportUserControl();
control.Children.Remove(...);
Problem is that there is no .Children.Remove() option for UserControl. How can i manage this?
UPDATE
I want to "send" this grid to another class and use it there. Problem is that when I send the grid and try to use it in another class I get this: "Specified element is already the logical child of another element. Disconnect it first." so I have to remove it from my UserControl but don't know how.
ReportUserControl XAML:
<UserControl x:Class="WPFReportTest.ReportUserControl "
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="29.7cm" Width="21cm">
<Grid Name="ReportGrid">
****GridContent***
</Grid>
</UserControl>
ReportClass:
public class ReportingClass
{
Grid reportGrid;
public ReportingClass(Grid tempGrid)
{
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
page.Content = reportGrid;
}
}
InstanceWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl.ReportGrid);
}
}
This is not the full code only small part to get idea of what I'm trying to manage. Line "page.Content = reportGrid;" is where the exception happens.

UserControl has Content property (msdn).
If you want to delete content set null:
ReportUserControl control = new ReportUserControl();
control.Content = null;
Example:
public class ReportingClass
{
ReportUserControl _reportUserControl;
Grid reportGrid;
public ReportingClass(ReportUserControl reportUserControl, Grid tempGrid)
{
_reportUserControl = reportUserControl;
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
// _reportUserControl is the instance of object ReportUserControl where content is reportGrid
_reportUserControl.Content = null;
page.Content = reportGrid;
return page;
}
}
Edit:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl, userControl.Content as Grid);
}
}

#Stojdza, in WPF, we generally don't manipulate UI elements in that way. We manipulate data elements and declare DataTemplates to define what that data looks like. So in your case, you shouldn't literally move the Grid... just move the data and let WPF regenerate your Grid wherever you want using the same DataTemplate. It will look just the same, but it's a whole lot easier and you'll save yourself all of this trouble.
So, if you're interested in doing this the easy way, first create a class that has all of the properties required in the UserControl, let's call it Data. Now declare a DataTemplate in Application.Resources so that you can use it application-wide:
<DataTemplate DataType="{x:Type YourNamespacePrefix:Data}">
<!-- Define your Grid here -->
<DataTemplate>
Now whenever you want to display your Grid, whether in a collection, or individually, you just need to bind a property of type Data (or a collection of them) to some form of ContentControl:
public Data Data { get; set; }
...
<ContentControl Content="{Binding Data}" />
So rather than trying to copy the Grid, you just copy the data to another property of type Data and recreate the Grid with the single line above. See the Data Templating Overview page on MSDN for more information on DataTemplates.

Related

Can't add children to Grid when binding to VM with Caliburn Micro

I want to add (any) elements to a grid from my view model in my Caliburn.Micro application (it's really a Revit add-in so not quite an application, but should work the same).
I'm having a hard time wrapping my head around certain aspects of the MVVM model and binding data to elements in the view...
My ViewModel
public class MyViewModel : Screen
{
private Grid _myGrid;
public Grid MyGrid
{
get { return _myGrid; }
set
{
_myGrid = value;
NotifyOfPropertyChange(() => MyGrid);
}
}
public MyViewModel()
{
MyGrid = new Grid();
var label = new Label { Content = "Hello!" };
MyGrid.Children.Add(label); // I know this isn't MVVM but how can I do basically this?
}
}
My View
<Window x:Class="MyProject.MyView"
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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:MyProject.Views"
mc:Ignorable="d">
<Grid x:Name="MyGrid">
<!-- Here goes stuff from view model -->
</Grid>
</Window>
I know this is all wrong... I need help getting elements into my grid element from the view model.
The general approach you are taking here is wrong as you are creating View elements in your ViewModel. With MVVM, you still want to design your UI (View) in XAML. In your case you would still create the Grid and the Label in your XAML in most cases. Think of what you are doing with this approach.
You have a data model that stores the data properties and notifies of changes. Think of this as the structural side of the data.
public class MyModel : INotifyPropertyChanged
{
private string labelValue;
public string LabelValue
{
get { return labelValue; }
set
{
labelValue = value;
NotifyOfPropertyChange(() => LabelValue);
}
}
//Property changed handler logic below.
//I assume you have something based on your code.
}
Now we create a ViewModel that does stuff with the data. This is what the view is bound too.
public class MyViewModel
{
//Add a property for the model you created
public MyModel NewModel {get;set;}
//Load the NewModel values when the view model is created.
public MyViewModel()
{
NewModel = new MyModel(){LabelValue="Hello World"};
}
}
Ok so we have a data Model, and we have a ViewModel to actually use the Model and fill it with values. Now lets create the UI (View) and bind to the ViewModel.
In your Window you will create a label and simply bind your desired ViewModel property. In this case we will your your models LabelValue property.
<Grid>
<Label Content="{Binding NewModel.LabelValue}"/>
</Grid>
Now we simply set the datacontext in the UI codebehind
public MyViewModel myViewModel = new MyViewModel(); //Create the ViewModel
public MyWindow()
{
InitializeComponent();
//Set the DataContext to the ViewModel
DataContext = myViewModel;
}
When you fire up the program the label content should be whatever you set it as in your ViewModel MyModel.LabelValue property. This is only a simple explanation of very large concept, but it should give you an idea of how it works. There are also many techniques and styles of MVVM people use, but this concept of is MVVM in its most basic form. Hopefully this gives you an idea and starts you on the right path.
The goal is to run your logic in you ViewModel so that the Views and Models can be built with minimal code. If you properly implement INotifyPropertyChanged all values on your View automatically update with no additional code or UI thread locks. Though the concept seems bulky at first, it saves a lot of time and effort in the long run.

WPF view who leads to another using MVVM

I am trying to set up a navigation between views using a MVVM pattern. My application contains a MainWindow and two views with a button each. When I click on the button in the View1 I want to set up the View2 on the MainWindow.
I have found several tutorials witch explain how to navigate from a view to another with a button on the main window (simulate a tabControl), it works but it is not what I want.
I'm looking for something like :
View1_View.xaml.cs :
public partial class View1_View : UserControl
{
private View1_ViewModel _viewModel = new View1_ViewModel();
public View1_View()
{
InitializeComponent();
}
private void Btn_SwitchToView2_Click(object sender, RoutedEventArgs e)
{
MainWindow.SwitchToView2();
}
}
MainWindow.xaml.cs :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new View1_View();
}
public void SwitchToView2()
{
this.DataContext = new View2_View();
}
}
My problem is if I do so, from the class View1_View I cannot access to the method SwitchToView2() if it is not static, and if it is static I lose the context of the MainWindow.
How should I proceed ?
Thanks.
I would recommend using a ContentControl to switch the part of your main view.
This could look like this (short form just to give you an idea; without INotifyPropertyChanged).
Create an empty interface of type ISwitchableViewModel.
Add a property to your main ViewModel
public property ISwitchableViewModel MyViewModel {get; set;}
Create two classes that implements the interface ISwitchableViewModel. Each for each view you want to show (View1 and View2 in your example) and call them ViewModel1 and ViewModel2.
When you press the button in your xaml set the MyViewModel to View1 or View2; whatever your logic is.
In your xaml add this at the place where you want to show the switchable content.
<ContentControl Content="{Binding MyViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewModel:ViewModel1}">
<view:View1 />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ViewModel2}">
<view:View2 />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
When you set the MyViewModel in your MainViewModelthe UI will show automatically the correct view for that viewmodel.
You can achieve this by creating the views and assigning them to a content control.
Lets assume you have this content control in your main view.
<Window x:Class="MVVM.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:MVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="ChangeView" Click="SwitchToSecondView" Content="Set View"></Button>
<ContentControl x:Name="MainContent"></ContentControl>
</StackPanel>
</Window>
You can then set the content in the code behind file of your main view.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void SwitchToSecondView(object sender, outedEventArgs e)
{
var view = new SecondView();
var model = new SecondViewModel(this);
view.DataContext = model;
MainContent.Content = view;
}
public void SwitchToThirdView(object sender, outedEventArgs e)
{
var view = new ThirdView();
var model = new ThirdViewModel(this);
view.DataContext = model;
MainContent.Content = view;
}
}
Another solution would be to use an MVVM Framework light Caliburn.Micro, Prism etc, which essential do the same thing as the code snippet above, but hide the boilerplate code.
EDIT: I realized i didn't explicitly get to the second part of your question.
Usally one would need some kind of router which is able to control the navigation. For the sake of simplicity we use the main view as router. To access the main view, you need to inject it in each component.
This allows each of your submodels to access the main view.
This could be improved by using some kind of DI-Container or by a Mediator. A mediator would allow each component to send requests, which then are dispatched to the MainView, eliminating the direct dependency.

Inject controls in derived windows breaks xaml markup in combination with binding

I am trying to inject a LayoutGrid and a canvas into my windows, but this causes a little headache:
Here is my WindowBase class:
public class WindowBase : Window
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
if (Content is FrameworkElement)
{
var originalContent = Content as FrameworkElement;
var grid = new Grid();
grid.DataContext = originalContent.DataContext;
Content = grid;
grid.Children.Add(originalContent);
var canvas = new Canvas() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };
grid.Children.Add(canvas);
}
}
}
My MainWindow thats inheriting from WindowBase looks like this:
XAML:
<local:WindowBase x:Class="InsertCanvasTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:InsertCanvasTest"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Border>
<Grid>
<ComboBox SelectedIndex="1"
ItemsSource="{Binding ItemSource1}" />
</Grid>
</Border>
</local:WindowBase>
Code Behind of MainWindow:
public partial class MainWindow : WindowBase
{
private List<int> _itemSource1;
public List<int> ItemSource1
{
get
{
if (_itemSource1 == null)
_itemSource1 = new List<int>(){1,2,3};
return _itemSource1;
}
}
public MainWindow()
{
InitializeComponent();
}
}
As you can see in my XAML I have specified that the SelectedIndex should be 1, but with the code in my WindowBase where I am trying to inject the Canvas and the Grid this information gets lost and the SelectedIndex is at -1.
Is there a way to fix this?
I would like to keep the MainWindow as a Window and not implement it as a control and load this into some different Window inside a ContentPresenter of so.
I know this problem wouldnt exist if I declared the Canvas/Grid in the XAML directly instead of trying to inject it in codebehind, but doing this with 100+ windows and trying to maintain them if something changes is annoying.
Change your WindowBase class like that :
WindowBase
[ContentProperty("InternalContent")]
public class WindowBase : Window
{
// InternalContent
public static readonly DependencyProperty InternalContentProperty =
DependencyProperty.Register( "InternalContent", typeof(object),
typeof(WindowBase), new FrameworkPropertyMetadata(null));
public object InternalContent
{
get { return GetValue(InternalContentProperty); }
set { SetValue(InternalContentProperty, value); }
}
...
}
<Window ...>
<Grid>
...
<ContentControl IsTabStop="false"
Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=InternalContent}" />
<Canvas />
...
</Grid>
</Window>
In essence, what it does is to create a new InternalContent property that subclasses will see as the default content (thanks to ContentPropertyAttribute), and display that content with a ContentControl.
That said, there is most likely better ways to do what you're trying to do than inheritance. Using templates comes to mind. Or maybe Adorners if what you want is an "above all layer to display things" like your Canvas suggest.
I suspect your problem is the setting of Content property of your Superclass Window to a "new" grid and then inserting the original content into that grid.
Where I think the problem may be is that you are setting the variable originalContent equal to Content BUT Content is an Object meaning it's a Reference Type. Problem shown below
var originalContent = Content as FrameworkElement;
//Reference Type: originalContent POINTS AT Content;
var grid = new Grid();
Content = grid;
//Reference Type: Content POINTS AT grid vsv. originalContent now POINTS AT grid
// Now because of Pointers and Reference Types
// originalContent = grid
If you want to preserve the Original Content in your Window Base Class using the Code you've shared, you would need to clone the control and use that reference in
var originalContent = CLONE OF CONTENT.
your variable originalContent has a parent and so kannt be added to a new Frameworkelement. You must remove originalContent from the old parent before you add it to a new one.

How to make a list of buttons dynamically and show them in the MainForm in a ListBox or ItemsControl

I am new in the WPF.
I want to make the list of buttons from a list of a class say "Buttons" and having two fields (ButtonContent,ButtonID) in the MainForm in WPF.
The idea behind this is that when the MainForm will be loaded then i want to make buttons list dynamically.
The Best Example is already given in the Link
http://www.codeproject.com/Articles/25030/Animating-Interactive-D-Elements-in-a-D-Panel
But i only want to make the equal size buttons stacked horizontally.
How can i do this in WPF? Thank you in advance.
This is the way I would do it. There are some areas here that will need further research on your part, but this will get you started.
First you need your ViewModel. This is the plain old object. It exposes the properties and methods that are pertinent to your business. You mentioned ButtonContent and ButtonID. Let's assume that both of those are strings for now. You'll also need a command for your button I assume.
public class ButtonViewModel : INotifyPropertyChanged
{
private string _content;
public string Content
{
get{ return _content; }
set{ _content = value; OnPropertyChanged("Content"); }
}
// you'll need to implement INotifyPropertyChanged
// also take a look at RelayCommand
// here is your command for you're button
public ICommand ButtonCommand
{
get { return new RelayCommand(execute, canExecute); }
}
private void execute()
{
// my actual command code
}
private bool canExecute()
{
// this will automatically toggle the enabled state on my button
}
}
You'll have one more view model. This will be the DataContext of your MainWindow.
public class AppViewModel
{
public ObservableCollection<ButtonViewModel> MyButtons {get; set; }
public AppViewModel()
{
MyButtons = new ObservableCollection<ButtonViewModel>();
// add some demo data
MyButtons.Add(new ButtonViewModel() {ButtonContent = "Click Me!"});
}
}
Now you need to implement the view. In the MainWindow xaml code. Add your xmlns:local namespace.
<Window.DataContext>
<local:AppViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding MyButtons}">
<ListBox.ItemsTemplate>
<DataTemplate>
<Button Command="{Binding ButtonCommand}" Content="{Binding Content}"/>
</DataTemplate>
<ListBox.ItemsTemplate>
</ListBox>
Hopefully this can get you started in the right direction.

Wpf - Update custom user control at runtime

hey guys i created an wpf application with custom usercontrol , the problem is that i am unable to update or say change the properties of custom control at runtime,
-->heres a code of user control
File:usercontrol.xaml
<UserControl x:Class="ExampleWpf.UserControlExample"
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"
Width="Auto" Height="Auto">
<Rectangle Width="60" Height="200" Fill="#FFB65959" Name="Box1"></Rectangle>
File:usercontrol.xaml.cs
namespace ExampleWpf{
public partial class UserControlExample : UserControl
{
public UserControlExample()
{
InitializeComponent();
}
public double Box1Width
{
get { return (Box1.Width); }
set { Box1.Width = value; }
}
}
--->Heres a simple code for Wpfapplication
File:Mainwindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
UserControlExample uc = new UserControlExample();
uc.Box1Width = 100;
}
}
All i wanted to change the width of a custom control at run time when user click on the button----> but unfortunately i doesn't succeed
please help me out
You are creating a new instance of your user control and not doing anything with it.
You should be finding the existing instance of the control and updating that.
You should also implement this type of behaviour through Dependency Properites. This gives you all sorts of advantages as outlined in the tutorial, but the main one here is that you can set properties like this at runtime.
Use the instance you placed in your xaml (f.e. through the Name-property), like so:
<uc:UserControlExample Name="myUC"/>
myUC.Box1Width = 100;
Another problem could be, that you just change the Width of the UC's rectangle. So lets say the UC has a Width of 50 and you call uc.Box1Width=100, the UC has still a Width of 50, so it wont change anything.

Categories