C# referencing a Grid in WPF to change properties - c#

Hello im new to making apps with WPF and XAML in Visual Studio. So I have a grid I want to change its properties in the code.
My Grid's properties:
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286">
How I have been trying to change its properties
this.usersPan.SetValue(Grid.WidthProperty, PAN_SIZE);
usersPan.SetValue(Grid.WidthProperty, PAN_SIZE);
usersPan.Width = 0;
usersPan.Visibility = Visibility.Collapsed;
When I try to do that^ it says null reference for userPan
Thanks

Noooooooo, Don't ever do that. Make a ViewModel that is bound to the Grid's Width property, and then just change the value.
My suspicion is that you do not need this at all. Have a look into containers, and how to position them.
In all of this years, there have been rare occasions I needed to do that and I suspect you do not need to. Tell me what you are doing.
EDIT:
You have a VM which needs to implement the NotifyPropertyChanged interface (I won't do that here, there are plenty of examples on hoew to do that)
public class MainVM
{
public ObservableCollection<TabVM> TabsVms {get;set;}
public int SelectedIndex {get;set}
}
bound to the control
<TabControl DataContext={TabsVMs} SelectedIndex="{Binding SelectedIndex}">
...
</TabControl>
And in runtime you create a couple of Tabs
var TabsVMs = new ObservableCollection<TabVM>();
TabsVMs.add(new TabVM());
TabsVMs.add(new TabVM());
TabsVMs.add(new TabVM());
Then in runtime you change the value of the index.
MainVm.SelectedIndex = 1
and the the coresponding tab will become selected.
EDIT:
I can also recommend you to use Fody for the MVVM notification.
Also, when it comes to bindings, I can recommend you to use WPF inspector. a handy little tool

The best way to write WPF programs is to use the MVVM (Model-View-View Model) design pattern. There are two (2) ideas behind MVVM:
Write as little code as possible in the view's code-behind and put all of the logic in the View Model object, using WPF's data binding feature to connect the properties of the View Model object to the view's controls.
Separate the logic from the display so you can replace the view with some other construct without having to change the logic.
MVVM is a huge topic on its own. There are lots of articles about it, and frameworks that you can use to build your program. Check out MVVM Light, for example.

Don't know exactly why Grid is invisible in code-behind, but You can access it's properties using events (but don't think it is perfect solution).
For example add to your grid event Loaded
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286"
Loaded="FrameworkElement_OnLoaded">
and then from code-behind you can access grid in next way:
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
if (grid != null)
{
grid.Width = 0;
}
}
Better solution :
Add some boolean property to your ViewModel like public bool IsGridVisible{get;set;}
And bind it to your Grid
<Grid HorizontalAlignment="Left"
Height="603"
Margin="0,51,0,0"
x:Name="usersPan"
VerticalAlignment="Top"
Width="1286"
Visibility="{Binding Path=IsGridVisible, Converter={StaticResource BoolToVis}">
where BoolToVis is converter which converts true to Visible and false to Hidden. You can define it in App.xaml like :
<BooleanToVisibilityConverter x:Key="BoolToVis" />

I was able to do something like this so I can change properties outside of an event.
private Grid userGrid;
private void onUserGridLoaded(object sender, RoutedEventArgs e)
{
userGrid = sender as Grid;
}

Related

Add EventListener to WPF control defined in Generic.xaml [duplicate]

I have a WPF control ParentWPFControl from a third party that I would like to inherit from (let's call the child class ChildWPFControl). In the process, I plan to override some of the back-end logic and parts of the front end styles. I can do the former just fine but I have problems doing the latter.
I attempt to use a xaml <-> xaml.cs structure for the child country, but that appears to be not allowed with the following warning from VS:
Partial declarations of 'ChildWPFControl' must not specify different base classes
Now, I suppose I can write a ResourceDictionary XAML and define the front end there, but that becomes a problem if I want to add event handlers to the XAML (at least I couldn't find a way to do that)
Another alternative I have is to define the override template directly in the objects that use the ChildWPFControl but that makes the design less modular.
A final alternative I can think of is to make a xaml <-> xaml.cs pair that is a XAML style container and then force the ChildWPFControl to use the ControlTemplate defined within through the back end event handler.
Anyway, what I am looking for is an elegant and modular solution for my problem. Any advice would be welcomed.
Thanks
There are a couple of steps necessary to completely override a WPF Control. Some are necessary some are optional depending on your needs. I will explain the two important ones for you:
Creating a new default style
Every WPF control has somewhere a default style which contains it visual representation and override properties. Now if you derive from control WPF still thinks you want to use this default style, to change that you change the DefaultStyle in a static constructor like this
class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
Now if you use MyButton WPF tries to find a Style for MyButton, not for Button. OverridesDefaultStyle is a property in a style which might also be handy at some points. Usually these default styles should be placed in a theme related xaml.
Event Handlers when overriding classes
It is correct in a ControlTemplate or Style you can't use the syntactic sugar of using event like Click="OnClick". The point is, that the visual representation is decoupled from the logic part as much as possible. There are other ways though to overcome this, using the OnApplyTemplate method. By overriding this you ask the template "Give me this control" and then you just add your events there.
override OnApplyTemplate()
{
var innerChild = Template.FindName("PART_InnerChild", this) as MyInnerControl;
if(innerChild != null)
innerChild.SomeEvent += OnSomeEvent;
}
Note: The name of these controls usually begin with a PART_ by convention, this can be seen in WPF basic controls aswell. Its a nice way to tell the designers "Without this control, the logic part might break". There is also the attribute TemplatePart but it is not really important, WPF doesn't care about it. AFAIK Expression blend does some with it, personally i use it to tell other people what kind of inner controls are absolutely necessary to make this control work.
Personal advice
Deriving from a class is usually the last step we do when trying to customize controls. Because a lot of work is necessary to fully make it work and it can be limiting in reusability, we try to avoid it, for example a good alternatives are besides template overriding and styling; attached behaviors.
Lastly,
The whole subject is covered in a nice MSDN article.
Hope that helps
You can create your user control as wrapper, containing base control. In this way you can change styles in xaml add some logic in C# for wrapped contrŠ¾l. But it's tediously process.
Edit:adding sample(wrapper for telerik:RadComboBox )
XAML:
<UserControl x:Class="Controls.SingleDictionaryValueSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CardControls="clr-namespace:Controls"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" MinWidth="150" MinHeight="25" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="25"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- customize visual for wrapped control -->
<telerik:RadComboBox x:Name="cb"
Grid.Column="0"
VerticalAlignment="Center"
SelectedValuePath="Key"
ClearSelectionButtonContent="Clear"
ClearSelectionButtonVisibility="Visible"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
SelectAllTextEvent="None"
OpenDropDownOnFocus="false"
IsFilteringEnabled="True"
TextSearchMode="Contains"
EmptyText="Select item"
telerik:StyleManager.Theme="Metro"
FontFamily="Calibri"
FontSize="14"
IsEditable="True"
Foreground="#666"
KeyDown="cb_KeyDown"
SelectionChanged="cb_SelectionChanged"
GotMouseCapture="cb_GotMouseCapture"
DropDownOpened="cb_DropDownOpened"
KeyUp="cb_KeyUp">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadComboBox},Path=ActualWidth}" Text="{Binding Path=Value}" />
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
<CardControls:ErrorInfo x:Name="errorInfoControl" Grid.Column="1" Visibility="Hidden"></CardControls:ErrorInfo>
</Grid>
</UserControl>
CS:
public partial class SingleDictionaryValueSelector : IMyCustomInterface
{
....
private void cb_KeyDown(object sender, KeyEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_KeyUp(object sender, KeyEventArgs e)
{
SearchExecute();
}
private void cb_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_DropDownOpened(object sender, EventArgs e)
{
...
}
...
}
It looks like you have your inheritance mixed up more than that it is not allowed. Your root element of your xaml must match the base class of your xaml.cs.
If you are defining the base class in the same project, you will not be able to use it as the base class in the xaml, because it itself is still xaml and not a compiled control yet. Some ways to solve this: You can compile it in a seperate project and reference it, you can compile the base class entirely in .cs instead of a partial class, or you can use some style wizardry. Here is a link with examples of the last two: http://svetoslavsavov.blogspot.ca/2009/09/user-control-inheritance-in-wpf.html

Switch DetailsTemplate in ListDetailsView between view and edit mode

I do have a ListDetailsView showing some data (lets say Company as a simple example here). Normally the details of a Company are shown as readonly. However, via the ListDetailsView.DetailsCommandBar it is possible to edit a Company (and also add a new Company). A clear separation between view and edit mode seems to be a good choice for the UI. I'm using a UserControl to show details of a Company.
So here are my questions:
Where should the differentiation between view- and edit-mode happen? I thought it is a good idea to have a CompanyDetailsControl and a CompanyDetailsEditControl and select between the two (both using the same CompanyDetailsViewModel). There are other solutions as well, for example, the CompanyDetailsControl could handle the edit- and view-mode internally.
Assuming that it is a good idea to switch between two UserControl, how can that be realized with the ListDetailsView.DetailsTemplate? I though it would be easy to use a DataTemplateSelector here, but that is only available for the ItemTemplate.
Not sure what code to provide to clarify my questions. So in case you need any code to better understand my question please leave a comment.
Note: I have never worked with UWP app, only applying MVVM pattern from WPF.
Straight line where the split should happen is not drawn. It often depends on the developer himself, which framework is used and more.
Me personally would go in way where UI handles UIs' things and ViewModel handles data only. That means the view is responsible for showing you the controls you are expecting to see/control the application. And when the view learns that property was changed, it should update how it looks.
Since the point we know the app will have edit & readonly modes, we should prepare all necessary UI components (UserControls, Pages, ...) to handle both those states. They would be binded to ViewModel that have base in BaseViewModel that already have this edit variable inside. So that each UI component know it can work with that.
Base view model:
abstract class BaseViewModel : INotifyPropertyChanged
{
private string mIsInEditMode;
public string IsInEditMode
{
get { return mIsInEditMode; }
set
{
if(mIsInEditMode == value) return;
mIsInEditMode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(IsInEditMode));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
All "normal" ViewModels inherit from it:
class CompanyViewModel : BaseViewModel
{ /* props and logic of company */ }
UI component (UserControl) would have either trigger (<Style.Triggers/>) or binded properties Visibility and IsEnabled to the BaseViewModel. Those bindings would handle this logic of showing/hiding and you have potential to control whole layouts, hide controls etc.
<UserControl d:DataContext="{x:Bind local:CompanyViewModel}">
<UserControl.Resources>
<local:BoolInverterConverter x:Key="BoolInvert"/>
</UserControl.Resources>
<Grid>
<Grid IsVisible="{Binding IsInEditMode}" IsEnabled="{Binding IsInEditMode}">
<!-- Controls for edit mode -->
</Grid>
<Grid IsVisible="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}"
IsEnabled="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}">
<!-- Controls for readonly mode -->
</Grid>
</Grid>
</UserControl>
Note: I've used property IsVisible, You would actually use Visibility with some custom converter.

What is the proper design for interacting with controls using MVVM where I need to do calculations based on XAML controls?

I am still a bit green in WPF. I am refactoring a sizable applications where all the work was done in the code behind. I am refactoring to use MVVM.
A bit about the application:
This application contains a single window, the single window and the pertinent pieces of the XAML are below:
<Grid>
<Border Style="{StaticResource ClipBorderStyle}" Name="ImageBorder">
<Grid Style="{StaticResource ClipGridStyle}" Name="ImageGrid">
<Image Name="KeyImage" Style="{StaticResource MainImageStyle}" Source="{Binding ImageSource}" Cursor="{Binding ImageCursor}"></Image>
<Canvas Name="KeyCanvas" Height="{Binding Source=KeyImage, Path=Height}" common:CanvasAssistant.BoundChildren="{Binding CanvasControls}"></Canvas>
</Grid>
</Border>
</Grid>
When the user clicks on the image, I drop a control onto the Canvas at the location where the user clicked. There can be many objects, and multiple control types. Currently I have a view model defined for each of the different control types and I keep an observable collection of them in a main view model. I am about half way through refactoring, and am realizing I still have a ton of work being done in the code behind, and am modifying the objects in the DataContext a lot. I am curious how I can move a lot of this out of the code behind into a more structured format (maybe into the view model, maybe another pattern). If it were simply modifying data, this would not be a problem, but in many cases I need to do transforms, and track the location on the image. The user can interact with the application using both their mouse and their keyboard (click to select an object, left arrow to move it, etc).
The Core Questions
When I have to to something like any of the following:
private Point TranslateImageCoordinatesToParentGrid(Point pointOnImage)
{
return KeyImage.TransformToVisual(ImageGrid).Transform(pointOnImage);
}
OR
private void Marker_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var Marker = (Marker)sender;
if (Marker.IsMouseCaptured)
Marker.ReleaseMouseCapture();
}
OR
private void Marker_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var Marker = (Marker)sender;
Marker.CaptureMouse();
_dataContext.SelectedObject = Marker;
Marker.Focus();
}
OR
private void controlMarker_OnMouseMove(object sender, MouseEventArgs e)
{
var controlMarker = (controlMarker)sender;
var controlDataContext = ((controlMarkerObject)controlMarker.DataContext);
if (!controlMarker.IsMouseCaptured) return;
var tt =
(TranslateTransform)
((TransformGroup)controlMarker.RenderTransform).Children.First(tr => tr is TranslateTransform);
var currentMousePos = e.GetPosition(ImageGrid);
// center mouse on controlMarker
currentMousePos -= controlDataContext.CenterOffsetFromTopLeft;
var v = _dataContext.Start - currentMousePos;
tt.X = _dataContext.Origin.X - v.X;
tt.Y = _dataContext.Origin.Y - v.Y;
var currentImagePos = TranslateImageGridCoordinatesToChildImage(currentMousePos + controlDataContext.TopLeftOffset);
controlDataContext.ImagePosition = currentImagePos;
}
Where is the appropriate place for this logic that interacts with both the view and the view model (and view models for the controls)?
Is the code behind the appropriate place for this?
Should I be using this eventing model where I defined the mouse events, or convert them to ICommands?
Is there a better/cleaner pattern to use for an application like this?
The single most important aspect of WPF that makes MVVM a great pattern to use is the data binding infrastructure. By binding properties of a view to a ViewModel, you get loose coupling between the VM and view and entirely remove the need for writing code in a ViewModel that directly updates a view. The data binding system also supports input validation, which provides a standardized way of transmitting validation errors to a view.
I didn't say that you have to honor the pattern, I just say that you do violate the MVVM pattern if you handle clicks in the code-behind of the view. That's a fact.
If you want to remove all this code from code behind I suggest to use Caliburn, or
System.Windows.Interactivity v4.0 for WPF:
Example with interactivity on your code:
<Button Content="Submit">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding SubmitCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
When you need to work directly with UI controls from the view it's perfectly fine to write that code in the code-behind of the view. As a matter of fact that is where it belongs.
Since your ViewModel is already DataContext of the view you can easily get a reference to VM from the View's code-behind (var svm = DataContext as SomeViewModel) and update VM properties, call VM methods/function or do anything else that is in the domain of the ViewModel and defined within the ViewModel.

create textblock programmatically

I want to create textblock programmatically
But it seems the code behind file don't build it.
EDIT
Inside Main.cs
public Main()
{
InitializeComponent();
}
public void generateUI(TypeOne item)
{
// Create first element
TextBlock authorText = new TextBlock();
authorText.Text = "Saturday Morning";
authorText.FontSize = 12;
authorText.FontWeight = FontWeights.Bold;
gridUI.Children.Add(authorText);
}
Inside Main.xml
<Page.DataContext>
<ViewModels:MainWindowViewModel/>
</Page.DataContext>
<Grid Width="Auto" Background="WhiteSmoke" x:Name="grid">
<Grid x:Name="gridUI" Margin="0,68,0,-37">
</Grid>
</Grid>
Inside MainWindowViewModel.cs
Main genUI = new Main();
IEnumerable<TypeOne> generateUI = //query variable
from x in _txnType
where x.Description == selectedTypeOne
select x;
foreach (TypeOne ui in generateUI)
{
genUI.generateUI(ui);
}
But the public void generateUI(TypeOne item) not creating the textblock.
I supposed, the Main.xml cannot read it since the DataContext is set to MainViewModel.cs
Please help.
The fact that you want to follow the MVVM pattern doesn't exclude dynamic view creation. However I always recommend that you use "logic-only" in the view model. That means that the VM still only contains logic for the view to interact with, but has absolutely no knowledge about the view and how it behaves.
Your view is the one using the view model and should adapt the view dynamically. This means that in the code-behind of your view, you have access to your view model and can customize the view according to your current view model. You can respond to changes in the view model as well since the view model will implement INotifyPropertyChanged (which you can intercept in your code-behind just as normal bindings would).
The question you should ask yourself is whether MVVM is the right patter for you here. If the UI always comes from a database, then where are the bindings defined? Also in the database? If so, what actual logic are you implementing in the view model? In other words: what is the point of a VM if there is no custom logic. In that case I recommend to use the view-only approach. If you need custom logic, use the approach with the code-behind I described above.

Getting parent of new tab after adding to bound TabControl (mvvm)

I'm adding a close button to my tabs using the following guide:
http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem
This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. I'm binding the tab control using mvvm, so the parent property is apparently not being set and giving me a null reference exception for the parent when the event tries to remove from it.
Here's the binding so you get the idea:
<TabControl Name="tabControl" Margin="0,22,0.2,-5.2" ItemsSource="{Binding Tabs}" Background="#FF4C76B2"/>
Heres where the tabs are being added.
private void AddTab(object tabName)
{
ClosableTab newTab = new ClosableTab();
newTab.Title = "title?";
//newTab.Header = tabName;
TextBox test = new TextBox();
test.Text = "CONTENT (" + tabName + ") GOES HERE";
newTab.Content = test;
Tabs.Add(newTab);
OnPropertyChanged("Tabs");
}
Here is the event where the null reference is taking place:
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
As I see it there are two options:
try to find another way to remove the tab (without the parent
property)
try to find a way to somehow set the parent property (which cant be
done directly, it throws a compiler error)
That doesn't sound like MVVM to me. We work with data, not UI elements. We work with collections of classes that contain all of the properties required to fulfil some requirement and data bind those properties to the UI controls in DataTemplates. In this way, we add UI controls by adding data items into these collections and let the wonderful WPF templating system take care of the UI.
For example, you have a TabControl that we want to add or remove TabItems from... in a proper MVVM way. First, we need a collection of items that can represent each TabItem:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(TestView));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
I'm just using a DependencyProperty because I knocked this up in a UserControl and I'm just using a collection of strings for simplicity. You'll need to create a class that contains all of the data required for the whole TabItem content. Next, let's see the TabControl:
<TabControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" />
We data bind the collection to the TabControl.ItemsSource property and we set the TabControl.ItemTemplate to a Resource named ItemTemplate. Let's see that now:
xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type System:String}">
<TabItem Header="{Binding}" />
</DataTemplate>
This DataTemplate defines what each item in our collection will look like. For simplicity's sake, our strings are just data bound to the TabItem.Header property. This means that for each item we add into the collection, we'll now get a new TabItem with its Header property set to the value of the string:
Items.Add("Tab 1");
Items.Add("Tab 2");
Items.Add("Tab 3");
Note that I included the System XML Namespace Prefix for completeness, but you won't need that because your DataType will be your own custom class. You'll need more DataTemplates too. For example, if your custom class had a Header property and a Content property, which was another custom class, let's say called Content, that contained all of the properties for the TabItem.Content property, you could do this:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type YourPrefix:YourClass}">
<TabItem Header="{Binding Header}" Content="{Binding Content}" />
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:Content}">
<YourPrefix:SomeUserControl DataContext="{Binding}" />
</DataTemplate>
So this would give you TabItems with Headers set and Content that comes from SomeUserControl which you could design. You don't need to use UserControls, you could just add more UI controls to either DataTemplate. But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface.
And finally, to answer your question in the proper MVVM way... to remove a TabItem, you simply remove the item that relates to that TabItem from the collection. Simple... or it would have been if you really had been using MVVM like you claim. It's really worth learning MVVM properly as you'll soon see the benefits. I'll leave you to find your own tutorials as there are many to chose from.
UPDATE >>>
Your event handling is still not so MVVM... you don't need to pass a reference of any view model anywhere. The MVVM way is to use commands in the view model. In particular, you should investigate the RelayCommand. I have my own version, but these commands enable us to perform actions from data bound Buttons and other UI controls using methods or inline delegates in the view model (where action and canExecute in this example are the CommandParameter values):
<Button Content="Close Tab" Command="{Binding CloseTabCommand}"
CommandParameter="{Binding}" />
...
public ICommand CloseTabCommand
{
get { return new ActionCommand(action => Items.Remove(action),
canExecute => canExecute != null && Items.Contains(canExecute)); }
}
So whatever view model has your Tabs collection should have an AddTabCommand and a CloseTabCommand that add and remove items from the Tabs collection. But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. Use a DataTemplate to specify it if it is a UI control.
You can find out about the RelayCommand from this article on MSDN.

Categories

Resources