|
I'd be inclined to simplify that by binding the inner grid's DataContext to the selected item from the list:
<Grid Grid.Column="1" DataContext="{Binding ElementName=NavigationListBox, Path=SelectedValue}">
...
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=ChartId}" />
...
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=ChartName}" />
...
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=ChartDescription}" />
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I'm having trouble understanding WPF. I've read many tutorials and reviewed many examples, but I still can't seem to grasp the basics. I'm hoping someone here can give me the push I need to get on track.
A little background: I manage a software product for my company. I'm an engineer, not a programmer, so I program the math stuff and delegate everything else. The product uses WinForms now, but the UI is due for an overhaul and I'm considering a switch to WPF. Thus, I'm trying to learn WPF well enough to plan a possible WinForms to WPF transition.
I've given myself this agenda:
1) As a warm-up exercise, create a WPF version of the About form which uses MVP, MVVM, or some such design pattern to display the application revision number as read from an About Model.
2) Make the About Window show a sample revision number when in design mode.
3) Change the revision number label into a text box that can write to the About Model, just to confirm that I understand two-way binding.
4) Create a WPF version of the Chart form. The Chart Window will be significantly more complicated than the About Window, since it must display a list box of chart names and fields whose values change depending on the list box selection.
I'm working on Item 1 right now. Below, I've pasted the code I've written so far. My question is: How do I change the
<TextBlock Text="v1.0.0.0" /> bit so the About Window shows the contents of its .Model.Version property when the window is displayed?
Here is my startup code:
using System;
namespace TestProject
{
public static class Startup
{
[STAThread]
public static void Main()
{
AboutModel aboutModel = new AboutModel { Version = "v1.2.3.4" };
AboutWindow aboutWindow = new AboutWindow(aboutModel);
aboutWindow.ShowDialog();
}
}
}
Here is my About Model:
namespace TestProject
{
public class AboutModel
{
public string Version { get; set; }
}
}
Here is my About Window XAML:
<Window x:Class="TestProject.AboutWindow"
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"
Title="About" Height="200" Width="300">
<StackPanel>
<TextBlock Text="Product Name " />
<TextBlock Text="v1.0.0.0" />
</StackPanel>
</Window>
And here is my About Window C# code:
namespace TestProject
{
public partial class AboutWindow
{
public AboutWindow() : this(new AboutModel()) { }
public AboutWindow(AboutModel model)
{
InitializeComponent();
_model = model;
}
private readonly AboutModel _model;
public AboutModel Model => _model;
}
}
|
|
|
|
|
A WPF "window" runs in the context of a WPF "application".
Start off right: use a WPF application "project" (created in Visual Studio) to get your coding off to the right start with a working application (and window) that you can modify / extend.
Visual Studio Community Edition 2015 is powerful and free.
|
|
|
|
|
Mr. Schmitz,
Thank you for the reply.
To clarify: I am using Visual Studio Community Edition 2015, and this is a WPF Application project. My AboutWindow is a working copy of the initial window. I seek to extend it by binding that one TextBlock to the AboutModel's Version property.
Did you mean to imply that I have not started off right? If I have not started off right, can you be more specific about what I have done wrong?
|
|
|
|
|
Is your app currently "running"?
The code you show is not enough to be able to tell if this is working code; e.g. there are missing using statements.
And your start-up code is not "typical".
|
|
|
|
|
Gerry Schmitz wrote: Is your app currently "running"?
Yes
Gerry Schmitz wrote: The code you show is not enough to be able to tell if this is working code; e.g. there are missing using statements.
Would it help for me to post the solution? Is there a way to do that on this forum? Regarding the using statements: I must have used Resharper to clear all unnecessary using statements. The code runs fine with just the using statements shown, though.
Gerry Schmitz wrote: And your start-up code is not "typical".
I tried to write the simplest possible code to instantiate an AboutModel and assign it to a field in the AboutWindow object before displaying the window. What is the typical approach?
|
|
|
|
|
(See my other post regarding a solution).
"Typical" means there's usually a "main window"; and dialogs, etc. run after that.
Without a "main window" an app will just exit.
For testing a dialog, I would consider a main window with a button that could invoke the dialog repeatedly (for testing and tracing purposes).
|
|
|
|
|
Okay, I follow you. I'll modify the project to start that way.
|
|
|
|
|
Try this:
<StackPanel>
<TextBlock Text="Product Name " />
<TextBlock Text="{Binding Version}" />
</StackPanel>
public AboutWindow(AboutModel model){
InitializeComponent();
_model = model;
this.DataContext = model;
}
modified 18-Dec-15 18:57pm.
|
|
|
|
|
That worked! And it was so simple. I can't tell you how many examples I've looked at today, and they are all much more complicated than that.
|
|
|
|
|
You probably struggled because you were looking at examples that define a full application rather than simple binding.
WPF is a very different beast to winforms, and binding is the glue that makes it work. When you have completed your POC and want to get into a proper structure you will need to pick a framework (MVVM Light, Prism etc) and work with that. Rolling your own framework is painful although there are a few here who have done exactly that.
We did the transition from winforms to Silverlight (subset of WPF for the web) and it was a good 3 months before we got productive and settled into MVVM Light. Once we got a good handle on the framework and added our extensions, productivity really took off.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Great!
There are a lot more aspects to "data binding" ... This is a simple case of one-way from source to target.
|
|
|
|
|
I really like coding for WPF and it is very powerful, but it requires a different way of thinking. It is worth learning (for your software developers).
So if you don't need anything specifically available in WPF but not in WinForms then I'd suggest that you stay with WinForms.
Don't risk the development effort (and time and money) to convert to WPF if you don't need to.
Your customers almost certainly don't care.
"Fairy tales do not tell children the dragons exist. Children already know that dragons exist. Fairy tales tell children the dragons can be killed."
- G.K. Chesterton
|
|
|
|
|
Mr. Heffron,
That is sound advice. Remaining on WinForms is an option, and I am often the strongest advocate for that option when this question comes up. However, I must acknowledge that there are many factors in play. We are overhauling the UI no matter what, so the WPF development effort must be measured relative to a WinForms development effort, not relative to doing nothing. Furthermore, I have been told that there are specific things available in WPF which I can't have in WinForms. We have one monster grid which is cobbled together from multiple grids and is a maintenance nightmare. I'm told that's the kind of thing where WPF's approach to binding makes things much simpler. Also, I remember asking for a dual range slider with some specific functionality and being told that it couldn't be done in WinForms, but was easy in WPF. And we have lots of layout glitches that emerge whenever someone is using a non-standard screen DPI. No one has been able to fix that in WinForms for me yet, but they tell me it isn't an issue in WPF.
Ultimately, WinForms vs. WPF is like any other choice. I'll look at all the facts and make the best decision I can. I sure would like to understand WPF better, though.
|
|
|
|
|
Do you prefer to have your ViewModels hold an instance of a DTO, then bind to that DTO's properties, or do you list out the properties you want to bind to and populate them from the DTO?
Why?
If it's not broken, fix it until it is
|
|
|
|
|
I don't use DTOs, I use collection properties of the view model, and make the child objects have the structure of the data I want to get to. Then I can just either iterate through the children for a list, or select using linq the child I need and use the properties of the child object
ChildObjectClass instance=ViewModelName.ChildObjectCollection.Where(c=>Someproperty==MatchThis Value).FirstOrDefault<ChildObjectClass>();
And in the HTML:
<p>Here is the value of my property: @instance.Property</p>
|
|
|
|
|
Hello Everyone,
I am having a listbox control in my app which is also having datatemplate inside and everything is fine until here. My problem starts when i want to change border background color on different databind for tag property of checkbox [please see datatemplate trigger in below code to understand what i am trying to achieve]. Please help me out.
Comment part in below code are the options i am trying to use but unfortunately not workking
<ListBox Name="listBoxDraftVersions" Height="200" Width="250" Margin="3" ItemsSource="{Binding DraftList}" SelectionMode="Multiple" SelectionChanged="listBoxDraftVersions_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="TheBorder" BorderBrush="Gray" BorderThickness="1" Padding="4" CornerRadius="4" Margin="2">
<CheckBox Name="radioButZone" Content="{Binding DraftName}" Tag="{Binding FunctionCode}" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}">
</CheckBox>
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="AliceBlue" Offset="0.15" />
<GradientStop Color="White" Offset="0.85" />
<GradientStop Color="Lavender" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<!--<Border.Style>
<Style>
<Style.Triggers>
<Trigger Property="" Value="CMDSDMODIFYFEATURES">
<Setter Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}, Path=Background}" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>-->
</Border>
<!--<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Tag}" Value="CMDSDMODIFYFEATURES">
<Setter TargetName="TheBorder" Property="Background" Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
|
|
|
|
|
It looks as if you aren't specifying the trigger correctly. It has a value to look for, but it doesn't know of what it is the value. Perhaps if you give it the ElementName and Property as well as the value, then it can know where to look for the situation in which to fire the trigger.
Joey
Joseph M. Morgan
Lairhaven Enterprises
Waynesboro, Virginia, USA
|
|
|
|
|
Hi Joseph,
Thanks for your prompt response but i am unable to understand what are you trying to say!! Sorry i am bit new in WPF
Can you please help me in fixing this code as i have already shared code in my last post. What exactly has to be done??
Regards
|
|
|
|
|
Saurabh18cs wrote: Sorry i am bit new in WPF
So am I, unfortunately. What I understand about a trigger is that it has to know three things: What condition fires it, what the source of that condition is, and what to do when it fires.
From my understanding of your code, you are setting the first and the third but not the second. Where you have
<Trigger Property="" Value="CMDSDMODIFYFEATURES">
I think you need to set that property, so it knows where to look for the value "CMDSDMODIFYFEATURES" on that property.
I hope that is clearer--I am going by a very new understanding of WPF.
Joey
|
|
|
|
|
Hi Joey,
Thanks for this explanation , so in my case this is what i am doing :
<!--<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Tag}" Value="CMDSDMODIFYFEATURES">
<Setter TargetName="TheBorder" Property="Background" Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>-->
My Doubts here??
1) Is datatemplate triggers is correct place to accomplish my work? or some other way like i tried border.style triggers??
2) If datatemplate trigger is a correct way then as per your definition of triggers
Binding="{Binding Tag}" --> This is source
Value="CMDSDMODIFYFEATURES" --> fire if value matches CMDSDMODIFYFEATURES
<Setter TargetName="TheBorder" Property="Background" Value="Blue" /> --> do this stuff
I can see all 3 conditions are meeting here , so please correct me if i am missing something or what is the correct way to do this..
Note:: my checkbox is holding these values i.e. CMDSDMODIFYFEATURES under tag property Tag="{Binding FunctionCode}"
Regards
Saurabh
|
|
|
|
|
Here is an example from "WPF Succinctly" which is a free e-book from Syncfusion.
(I am not connected to them, so I am putting their library link here for everyone.)
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Border" Property="Background" Value="Purple" />
<Setter TargetName="Border" Property="BorderBrush" Value="DarkKhaki" />
</Trigger
According to the book, the data template or control template is the place to put the triggers.
Note that the property IsPressed is the property to be evaluated. In your case, it is the value of the check box, if I recall correctly. The Value property is the value you want that target property to have in order to fire the trigger. Without knowing your data structures, I am not sue what the binding is doing in your example. I am also not sure why you are using a tag instead of the value of the checkbox. If you use the template for the checkbox control, and the value of "true" is what you want to trigger the modifications, then you should be using this data template for the checkbox and use "True" on the Value property, I would think.
If I am wrong and you are selecting from multiple items as in a radio button group, then you would need to evaluate the value of the group and the trigger would be set on that.
Mind you, I am very new to WPF as well, so this is just my take on what the experts have written. I recommend both this book and some of the tutorials on line through Packtpub and Safari Online, if you have subscriptions to either.
Joseph M. Morgan ("Joey")
Lairhaven Enterprises/Solstice Systems
Waynesboro, Virginia, USA
|
|
|
|
|
Hi Joseph,
Thanks again for your response.
As a answer of your question, i am using tag property of checkbox because content property is already taken by another another binding property which i want to show on my checkbox. I want to fire trigger based on the tag property of checkbox because i have binded tag property to second property of binding.
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding FunctionCode}" Value="CMDSDMODIFYFEATURES">
<Setter TargetName="TheBorder" Property="Background" Value="{StaticResource MenuBackground}" />
</DataTrigger>
</DataTemplate.Triggers>
as per your post i have to use any property of datatemplate to fire, but what would be that property in my case? I am only passing binding and value as of now to my datatemplate.trigger
Wish you a very happy new year
|
|
|
|
|
Please help .. i am still not able to find way out
|
|
|
|
|
You're attempting to bind to a property that doesn't exist. Your Tag property is actually a property of the CheckBox which is, itself, bound to a different property on your ViewModel. So, you need to set your Trigger to use the underlying property. In other words, replace Binding="{Binding Tag}" with Binding="{Binding FunctionCode}" . If you'd checked your Output window when debugging your application, you'd probably have seen a binding error telling you that this was missing.
This space for rent
|
|
|
|