|
You need to look into the future of silverlight before making that decision - it does not have one since the plug in model will no longer be supported!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Once you get a hang of data binging and INotifyPropertyChanged , you should not need to use the dispatcher.
Though a little off-topic, you can also explore attached behaviours[^] in your spare time. These help you get around problems that you could come across while handling events.
|
|
|
|
|
I see that you alread have gotten some very useful comments in this question. I found that If I set the binding in code behind it is easyer to do ceirtain things, like:
Sub SetObjectBinding(ByVal Source As Object, ByVal Path As String, ByVal control As TextBox)
Dim bnd As New Binding
bnd.Path = New PropertyPath(Path)
bnd.Source = Source
bnd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
bnd.Converter = New DoubleToStringConverter
bnd.ValidationRules.Add(New PositiveDoubleValidator)
control.SetBinding(TextBox.TextProperty, bnd)
End Sub
With the classes:
<ValueConversion(GetType(Double), GetType(String))>
Private Class DoubleToStringConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
Return value.ToString
End Function
Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Dim d As Double
Dim str As String = value.ToString
If str.Contains(".") Then str = str.Replace(".", ",")
If Double.TryParse(str, d) Then
If d > 0 Then
Return d
Else
Return DependencyProperty.UnsetValue
End If
End If
Return value.ToString
End Function
End Class
Public Class PositiveDoubleValidator
Inherits ValidationRule
Public Overloads Overrides Function Validate(value As Object, cultureInfo As System.Globalization.CultureInfo) As System.Windows.Controls.ValidationResult
Dim d As Double
Dim str As String = value.ToString
If str.Contains(".") Then str = str.Replace(".", ",")
If Double.TryParse(str, d) Then
If d > 0 Then
Return ValidationResult.ValidResult
Else
Return New ValidationResult(False, "The entered value must be positive")
End If
Else
Return New ValidationResult(False, "The value '" & str & "' could not be converted to a positive number")
End If
End Function
End Class
And in the XAML code:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="5">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
I dont know exactly how this could be done in MVVM but this works for me at least.
|
|
|
|
|
Thanks Kenneth
I will look at this code some evening this week - looks like it will be of great help.
As you say the responses have been very helpful and I am glad that binding is the way to go as using the despatcher feels like a bit of a workaround.
“That which can be asserted without evidence, can be dismissed without evidence.”
― Christopher Hitchens
|
|
|
|
|
Obviously the checks could be made better, but the idea is still the same, and it works quite well for my puropses. 
|
|
|
|
|
I have been working on a WPF project. The requirements changed to include a Windows 8 Tablet, so I have created both PC and Win 8 Tablet versions. To do this I split the WPF project into 3:
UI.WPF.PC
Contains the views used in the PC version of the application.
UI.WPF.Tablet
Contains the views used in the Windows 8 Tablet version of the application.
UI.WPF.Shared
Contains the components common to both the PC and Tablet projects. This includes the ViewMmodels.
So far, when a Window needs to open a dialog, I have been doing this from inside the Window's VM:
MaterialSizeEditorViewModel vm = new MaterialSizeEditorViewModel();
vm.Load(args);
MaterialSizeEditorView view = new MaterialSizeEditorView();
view.Owner = engine.MainWindow;
view.DataContext = vm;
view.ShowDialog();
As far as other views, I have done things like this:
protected override void LoadView(View viewType, _ArgsBase args = null)
{
dynamic vm = null;
UserControl viewToLoad = null;
string headerText = string.Empty;
if (TabManager.IsTabOpen(viewInfo))
{
TabManager.ActivateTab(viewInfo);
}
else
{
switch (viewType)
{
case View.Home:
headerText = "HOME";
vm = new TabletHomeViewModel();
viewToLoad = new TabletHomeView();
break;
case View.JobPhases:
headerText = "JOB PHASES";
vm = new JobPhaseViewModel();
viewToLoad = new JobPhaseView();
break;
case View.JobAssignments:
headerText = "JOB ASSIGNMENTS";
vm = new EmployeeJobAssignmentsViewModel();
viewToLoad = new EmployeeJobAssignmentsView();
break;
}
if (vm == null)
{
return;
}
vm.Load(args);
viewToLoad.DataContext = vm;
}
}
For data operations, the ViewModels call through a Web API Proxy in an engine class. The engine class is based off an interface with an instance of it passed into the CTOR of each ViewModel, such as:
public JobCenterViewModel(IEngine engine)
:base(engine)
{
engine.APIProxy.SomeMethod(some param);
}
The way I'm opening view is clearly wrong. Given the 3 projects, what's the right way to open a view or dialog?
If it's not broken, fix it until it is
|
|
|
|
|
The first question(s) I'd ask you if you were asking me are:
1) what's the difference between the PC version and the tablet version?
2) why are you assigning VMs from code? ViewLocator is your friend.
I'd have to assume that the PC and Tablet version are pretty similiar, but maybe have a different look and feel? If they are completely fundamentally different, I'd have to say one or both are poorly designed. You want people to be able to use both versions without having to learn two completely different UIs (LEARN FROM MICROSOFTS MISTAKE -- PEOPLE HATE WINDOWS 8).
If the main difference is the look and feel, you are kind of missing the whole point of WPF. There is no need to have two separate VMs. You simply apply a different template to the view. If you need a couple of different properties for the tablet or PC version, there is no harm in having it there in both (assuming you are doing it right and all your properties are lazy loaded).
If you have trouble sleeping at night because there are a few unused props in each version, you can ifdef them out depending on the build configuration .
If you really have your heart set on splitting the VMs, again, ViewLocator is your friend. You would load the correct XAML and the XAML would instantiate the proper VM through ViewLocator.
|
|
|
|
|
SledgeHammer01 wrote: 1) what's the difference between the PC version and the tablet version?
The Tablet exposes only pieces of the PC version. The look of those parts is also different. So that's why I have the Shared project - to hold the ViewModels that are common to both.
SledgeHammer01 wrote: 2) why are you assigning VMs from code? ViewLocator is your friend.
Because I don't know better
SledgeHammer01 wrote: There is no need to have two separate VMs.
I don't have separate VM's. Again, the shared project holds the VM.
When you say ViewLocator, do you mean this?[^]
If it's not broken, fix it until it is
|
|
|
|
|
Yup. That's ViewLocator. ViewLocator and DI are important to know. Really cleans up the code.
|
|
|
|
|
Ok, maybe I'm just not seeing it, but I can't find a decent example of this. All the examples I'm finding don't really seem to apply to what I'm doing.
Again, here's the problem I'm trying to solve...
The PC version looks like this[^]
Each 'Center' is essentially more or less the same - a list and tab control which can have any number of the view open. In the image, the Job Center is open with one Job open. The area in red is the JobView. The code in the JobCenter's LoadViiew (see below) method creates an instance of the JobViewModel and JobView, assigned the VM to the View's DataContext, assigns the view to the tab's Content property, and add the tab.
The Tablet's Home[^] is different. It's Job View[^] is much smaller and has less elements.
Here's where my confusion/problem lies...
Both Job views can use the same VM, so the VM is in the shared project.
Both Job views need to open dialogs. For example the Address Editor. In the PC version this is a model dialog. In the Tablet version is will be another 'flat' view that replaces the current view. The user will add or select an address, click Ok, the view goes away and the Job view comes back.
1) What's the right way to load the Address Editor?
2) The Center's all load their child views onto tabs like this:
private void loadView(JobArgs Args)
{
ViewInfo tabInfo = new ViewInfo
{
View = View.Job,
EntityId = Args.Job.JobRevisionId
};
if (tabInfo.EntityId > 0 && TabManager.IsTabOpen(tabInfo))
{
TabManager.ActivateTab(tabInfo);
}
else
{
JobViewModel vm = new JobViewModel();
vm.Load(Args);
UserControl view = new JobView();
view.DataContext = vm;
TabItem tabItem = new TabItem
{
Header = vm.GetEntityCaption(),
Content = view,
Tag = tabInfo
};
TabManager.AddTab(tabItem, true);
}
}
The obvious problem here is that the ViewModel knows about the View.
So how do I have the VM load the right view? regardless of which version I'm in. I'm confused because the VM lives in the Shared project, and the target views are in their own project. We discussed the ViewModelLocator, but I cannot find an example that seems like it works in this scenerio.
I'm guessing the ViewModelLocator's job is to say, "Hey I'm view 'JobView' and I need a view model, so go get it for me"?
While I like the idea, how and where do I handle opening dialogs, if not in the Job's VM?
I'm pretty confused about the right way to open/work with child views.
Let me ask the question this way, so I'm clear...
In the PC version, when the New button is clicked in the JobCenter, I need to add a tab and put a JobView control on a it. I have been doing all of this in the VM, but it's wrong. What is the right way to do this? An example would be great, if you know of one.
Thanks
If it's not broken, fix it until it is
|
|
|
|
|
If I do a tabbed view, it really depends on if the tabs are hard coded or dynamic.
If its hard coded, I hard code it in the XAML (psuedo code):
<TabControl>
<Tabs>
<Tab>
<ChildView1 />
</Tab>
<Tab>
<ChildView2 />
</Tab>
<Tabs>
</TabControl>
The XAML for ChildView1 and ChildView2 would use the ViewLocator to find the VM. Something like:
<UserControl somenamespace:ViewModelBase.ViewModel="{x:Type local:DocumentMapCtrlVM}" ... />
your ViewLocator should wire up the DataContext, etc. as shown in that link.
If its dynamic (or determined at runtime), you'd need to build a collection of some object where you have the name of the tab and the usercontrol instance for example and then bind the tab content to the object.UserControl from the collection with a data template.
For opening a dialog, I like to use a dialog service. That kind of needs a DI engine / container. You'd register a generic base interface:
interface IDialogService
{
AddressObject ShowAddressDialog(Window owner);
}
for example... in the tablet version, you'd register a version that slides it in like you are doing now and in the desktop version, you'd register a version that shows a popup dialog version instead, but its all the same interfaces, so your calling code is the same for both versions.
The end result of putting ViewLocator, DI and services together is that it solves all your problems lol... but you are really covering a LOT of ground with those 3 items and its not really something I can explain in 5 sentences lol..
|
|
|
|
|
Wow, well maybe I'm not too far off then. Consider...
SledgeHammer01 wrote: If its hard coded, I hard code it in the XAML (psuedo
code): <TabControl>
<Tabs> <Tab>
<ChildView1 />
</Tab> <Tab>
<ChildView2 />
</Tab>
<Tabs> </TabControl> The XAML for ChildView1 and
ChildView2 would use the ViewLocator to find the VM. Something
like: <UserControl
somenamespace:ViewModelBase.ViewModel="{x:Type local:DocumentMapCtrlVM}" ...
/> your ViewLocator should wire up the DataContext, etc. as
shown in that link. If its dynamic (or determined at runtime),
you'd need to build a collection of some object where you have the name of the
tab and the usercontrol instance for example and then bind the tab content to
the object.UserControl from the collection with a data template.
1) I already do all this
SledgeHammer01 wrote: For opening a dialog, I like to use a dialog service. That kind of needs a DI
engine / container. You'd register a generic base
interface: interface IDialogService {
AddressObject ShowAddressDialog(Window owner); } for example...
in the tablet version, you'd register a version that slides it in like you are
doing now and in the desktop version, you'd register a version that shows a
popup dialog version instead, but its all the same interfaces, so your calling
code is the same for both versions.
2) I'm already doing this.
But think of this... I think my problem is that I don't need a ViewModelLocator - I need a ViewLocator...
Since both the PC or Tablet JobViews are using the same VM, the only questions is which View to load.
What do you think?
If it's not broken, fix it until it is
|
|
|
|
|
At some point you're going to have to make a true / false decision in your code for tablet vs. PC. I have a UI library that styles differently in XP vs. Windows 7 for example. I have a single, global static DP and I select the proper template in XAML via a data trigger based on that DP. You can do something similiar. Apply the PC view by default and switch it out for the tablet view via a data trigger if the global IsTablet DP is set.
|
|
|
|
|
<blockquote class="FQ"><div class="FQA">Kevin Marois wrote:</div>Let me ask the question this way, so I'm clear...<BR> <BR>In the PC
version, when the New button is clicked in the JobCenter, I need to add a tab
and put a JobView control on a it. I have been doing all of this in the VM, but
it's wrong. What is the right way to do this? An example would be great, if you
know of one.<BR></blockquote>
I forgot to address this part in my original response.
Basically, your New button would send a command to the VM and the VM would add an object to its "TabItemCollection" that the tab control is bound to. Stylized with the DataTemplate of course to bind the properties correctly.
So you might have something like:
class ViewInfo
{
string Title;
UserControl UserControl;
}
ObservableCollection<ViewInfo> Tabs;
the tab title would be bound to the ViewInfo.Title property. and the Tab content would be bound to the ViewInfo.UserControl property.
|
|
|
|
|
How to perform file transfer same like as it happen in teamviewer and ammyy before in wpf
i want to open a window in which on the left side i get complete directories of my own system and on the right side i get all the directories of remote system in wpf using c#
can anyone halp me in it, to give me just an idea only
i dont want to access any remote system using theri network username and password i jst want to access it through their ip addresses
someone please help me in it, to give me an idea on it
|
|
|
|
|
Arun kumar Gautam wrote: i dont want to access any remote system using theri network username and password i jst want to access it through their ip addresses Then you will need a client application (like an ftp server) on the remote system that will give you the directory details and manage the data transfers.
Veni, vidi, abiit domum
|
|
|
|
|
bt ftp also needs client system username and password
|
|
|
|
|
Then you need to write your own client agent that allows you to connect without it.
Also please use the correct forum in future, this is not a WPF/Silverlight issue.
Veni, vidi, abiit domum
|
|
|
|
|
Hi,
if you do use Socket Programming, there no need to credential of Remote server just use ip address.
mohit Saxena
|
|
|
|
|
thnks mohit i really appreciate ur help.
|
|
|
|
|
But remember, you will still need a program on the remote system that will listen for and accept socket connect requests.
Veni, vidi, abiit domum
|
|
|
|
|
Hi... So I'm creating a column collection for a data grid in the code behind because I don't know the number of columns needed until run time. I need to bind the background color to a converter but i'm not sure how to do it. The background property is typed as a Brush, but of course the converter is a binding... so how do I get this working? In the code below, the "DataMemberBinding" works because that property is a binding type... So I just need to get the background working.
Thanks,
Sam.
Dim c = New Telerik.Windows.Controls.GridViewDataColumn With {.Header = sched.ShipName,
.IsReadOnly = True,
.IsFilterable = True,
.IsSortable = True,
.Background = New System.Windows.Data.Binding With {.Converter = New BackgroundColorConverter(),
.ConverterParameter = (s + 1).ToString},
.IsGroupable = False,
.ShowFieldFilters = True,
.ShowDistinctFilters = False,
.DataMemberBinding = New System.Windows.Data.Binding With {.Converter = New ColumnConverter(),
.ConverterParameter = (s + 1).ToString}
}
columnCollection.Add(c)
|
|
|
|
|
Try something like this:
Dim column As New GridViewDataColumn With
{
.Header = sched.ShipName,
.IsReadOnly = True,
.IsFilterable = True,
.IsSortable = True,
.IsGroupable = False,
.ShowFieldFilters = True,
.ShowDistinctFilters = False,
.DataMemberBinding = New Binding With
{
.Converter = New ColumnConverter(),
.ConverterParameter = (s + 1).ToString
}
}
Dim backgroundBinding As New Binding With
{
.Converter = New BackgroundColorConverter(),
.ConverterParameter = (s + 1).ToString},
}
column.SetBinding(GridViewDataColumn.BackgroundProperty, backgroundBinding)
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks for the reply. SetBinding doesn't exist on the GridViewDataColumn type though... I did figure it out, however:
Dim c = New Telerik.Windows.Controls.GridViewDataColumn With {.Header = sched.ShipName,
.IsReadOnly = True,
.IsFilterable = True,
.IsSortable = True,
.IsGroupable = False,
.ShowFieldFilters = True,
.ShowDistinctFilters = False,
.DataMemberBinding = New System.Windows.Data.Binding With {.Converter = New ColumnConverter(),
.ConverterParameter = (s + 1).ToString}
}
Dim backgroundBinding As New System.Windows.Data.Binding With {.Converter = New BackgroundColorConverter,
.ConverterParameter = (s + 1).ToString}
backgroundBinding.Source = c
System.Windows.Data.BindingOperations.SetBinding(c, Telerik.Windows.Controls.GridViewDataColumn.BackgroundProperty, backgroundBinding)
columnCollection.Add(c)
|
|
|
|
|
USAFHokie80 wrote: SetBinding doesn't exist on the GridViewDataColumn type
The BindingOperations.SetBinding method is an extension method. If you import the System.Windows.Data namespace, you'll be able to call it as c.SetBinding(...) .
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|