Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


I have data I need to process with record times at nanosecond resolution (specifically, measured in nanoseconds since the start of the year the data was recorded in, stored as uint64). For now I just need to turn it into a human-readable date and time, like 2012 May 28 11:35:58.000045921 (edit: adjustments will need to happen later, but that's not an immediate requirement). I figured at first (being a .NET and C# newbie) that DateTime would be able to do it with some finagling, but I guess from google that it only can store down to the microsecond, chopping off the 921 nanos at the end there. (Apparently they're important. :shrug:) (edit: or is it 0.1 microsecond? Anyway still two digits short in that case)

Do I have any options short of, say, making the DateTime with the microsecond precision out of the uint64 nanos, ToString()ing it, and just tacking nanos % 1000 to the end of the string? Strikes me as maddeningly clunky, even if it'd work for now.

Ciaphas fucked around with this message at 00:25 on Jul 12, 2014

Adbot
ADBOT LOVES YOU

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Bognar posted:

DateTime precision only goes down to the 100 nanoseconds. If you just want to use the built-in formatting DateTime formatting options, then I would say to make a class that wraps DateTime (e.g. PreciseDateTime) to delegate .ToString() calls and use whatever level of precision you need.

Thanks, I had a feeling it was gonna come down to that but I just wanted to make sure. I guess overriding all the calls I care about for actually manipulating those times won't be too awful.

drat sight better than fuckin' struct tm and time_t horsehockey, anyway. Going from C/C++ for over a decade to learning how to C#/.NET is kind of eye-opening. :v:

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


I have a List<DataRecord> dataRecords that I want to display in a WPF DataGrid. DataRecord is defined more or less as follows:

C# code:
public class DataRecord
{
   public ulong Yeartime { get; set; }
   public List<DataItem> Items {get; set; }
}

public class DataItem
{
   public MetaData Info {get; set;}
   public object Data {get; set;} // any of IntX, UIntX, single, double, byte[], char[]
   public string DataAsString { get { /* ... */ } }
}

public class MetaData
{
   public string Name {get; set;}
   public DataType Type {get; set;} // enum, not important
   // ...
}
This info is being filled by reading a file, which is where I learn the number of DataItems in a single record and their metadata (and of course the data itself).

Setting my DataGrid's ItemSource to dataRecords does more or less what I expected: give me two columns of Yeartime and Items, the former being the numbers I expect and the latter being "(Collection)".

I want to programmatically break out that List<DataItem> each into their own column, with the header being item.Info.Name and the cell data being item.DataAsString.

How would I go about this? Binding is a confusing and horrifying subject to me, I'm just barely getting started with C# and .NET let alone WPF :ohdear:

Ciaphas fucked around with this message at 21:01 on Jul 16, 2014

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ithaqua posted:

You can either define a template column to display the additional stuff, or let part of your viewmodel's behavior be to transform and project the data into a type intended for display.

Option 1:
code:
//SomeRecords is an ObservableCollection<DataRecord>

<DataGrid ItemsSource="{Binding SomeRecords}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Grid>
                <TextBlock Text="{Binding Yeartime}"/>
                </Grid>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <DataGrid ItemsSource="{Binding Items}"/>
                    </Grid>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

I ended up (after a lot of fuss and bother) going for Option 2, sort of. I turned off autogeneration in the datagrid, made its ItemsSource my list of DataRecords, and, in my xaml code-behind, generated, bound, and added the individual columns by selecting out the name and numeric index of each DataItem.

I don't have the code in front of me but it was something like this.
C# code:
// AllDataRecords is List<DataRecord>
var columns = myViewModel.AllDataRecords.First().Select((x,i)=>new { Name = x.Info.Name, Index = i });

foreach (var c in columns)
{
   Binding b = new Binding(string.Format("Items[{0}].DataAsString", c.Index));
   dataGrid.Columns.Add(new DataGridTextColumn() { Header = c.Name, Binding = b });
}
I don't know how robust it is, or if there was a LINQ alternative to that particular Select syntax, or how much of this is gonna change later, or even if this is remotely idiomatic or not, but it does work. Now I've got the problem that the DataGrid is very very slow to scroll (some 200mb of data, with 20 columns and 500k rows), but one step at a time I suppose.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Yeah, I sort of realized that wasn't quite the Option Two being presented after I posted. I'm new to the whole MVVM thing too though--in fact this is literally my first go at making GUI anything since Visual Basic 6.0 in high school--so it took me a while to realize. :(

I tried for several hours today to get binding to work in the XAML, but I kept getting blindsided mostly by the fact that the number of Items in each DataRecord, though the same across records, is completely unknown until runtime. (Their types are unknown, too, but that's what that DataAsString property is for :v:) What can I do in the XAML to set up those column definitions without that knowledge?

(The fact that my data is essentially a list of lists with some metadata leaves me confused too, but I'm sure the solution to that problem is simpler.)

Ciaphas fucked around with this message at 05:02 on Jul 17, 2014

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ithaqua posted:

Yeah, the Option #2 I was talking about was more along these lines:

You have your collection of DataRecords. Those obviously aren't intended for display as-is -- it's some sort of DTO. It represents the raw data you got back from something.

You should be, at some point prior to that object hitting your view, transforming it into a model intended for actual display. Automapper can help here, or you can do it by hand. Once you have a nice, simple model of the data that's intended for display, you just bind that to your data grid and you're golden.

Thanks, I'll look up Automapper. This explanation kind of makes more sense with what I understand about MVVM anyway.


Mr. Crow posted:

You never really explained why Ithaqua's example failed you? It should have worked generally as-is; the second column wouldn't look exactly like you want, but nothing some header styles and templates can't fix.

Yesterday it displayed absolutely nothing so I abandoned it, but I tried it again this morning and it worked :pwn:. I must have typoed the first ItemsSource or something and not noticed.

As you say, it looks like arse, but it does work, and I can probably figure out how to mess with those data templates. I'm going to try the second option as Ithaqua described it first, since that reorganizing will help me later anyway.

(Context, I'm trying to make a graphical diff application for the files we're working with. Up to now we've used a utility to convert them to CSV and throw those into Beyond Compare, which works but has a few important limitations that make it a pain in the rear end. So I figured I'd cut my teeth on learning C#, .NET, MVVM, and WPF all at once by putting this together.

I may have overreached myself :suicide:)


(edit) Oh, AutoMapper is third party? That's out then, my work machine has no internet access (don't ask).

I'm a little stumped at the moment at how to go about it manually. If I'm understanding things, I'd want to flatten what I've got above into something like this, right?

C# code:
class DataRecordDisplay
{
   public ulong Yeartime {get; set;}
   public string NameOfItem1 {get; set;}
   public string NameOfItem2 {get; set;}
   // etc.
}
Problems are, the autogenerated column headers are named by the name of the properties, so I'd have to name the properties at runtime somehow. Ditto not knowing how many properties this class would even have until runtime. That almost sounds like I'd have to learn some Reflection stuff, but that's making a bit of a mountain out of a molehill I think.

I'm sure I'm being stupid and there's a simpler way to go about it :(

Ciaphas fucked around with this message at 19:09 on Jul 17, 2014

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Following up on my previous problem, once a friend told me about System.Data.DataTable, reorganizing the data became more logical in my head. Ended up being something like this code in my viewmodel: (again, typed from memory so probably wrong)

C# code:
MyDataTable = new DataTable();

MyDataTable.Columns.Add("Time");
var q = from i in LeftFileContents.First().Items select i.Info.Name;
MyDataTable.Columns.AddGroup(q.ToArray());

foreach (DataRecord i in LeftFileContents)
{
  DataRow r = MyDataTable.NewRow();
  
  // add i.Yeartime to the row here, I forget the exact code

  foreach (DataItem j in i.Items)
  {
    // add j.DataAsString to the row, I forget the exact code
  }
  MyDataTable.Rows.Add(r);
}
And then set my datagrid's ItemsSource to MyDataTable and I was golden.

Made more sense to me than messing around with column templates, and seems more idiomatically correct than dynamically adding columns to the datagrid in the code-behind.

It worked, but am I more or less on the right track here with respect to doing it "right"?

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


In the diff application I'm trying to write in WPF, I have two bool arrays, RowDifferent[RowCount] and ColumnDifferent[ColumnCount]. In the DataGrids displaying the data I'm diffing, I want the following style:

- If RowDifferent[cell's row number] XOR ColumnDifferent[cell's column number], color background light pink
- Else If RowDifferent[cell's row number] AND ColumnDifferent[cell's column number], color background deep pink

I know I have to use a style datatrigger, somehow, but I'm not really sure how to bind the trigger to an array of data. Any thoughts?


(Sorry I can't paste the XAML here, work computer's not on the internet. :()

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Is it possible to have a WPF application, depending on certain command line options being present, not bother with the Windows part and just run in the current console (or a new one if run from a shortcut)? I have a project that a customer wants to add some scheduled processing to.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Are there no chart/graph/etc. controls built in to .NET/WPF by default? I've seen downloadable controls but I can't use those on my work machine.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Anyone have any recommendations for WPF charting/plot/graphing controls that are free (and preferably open source)?

I've tried Oxyplot already; while it basically works, trying to make my lineplot zoom to the actual data automatically when it changes is making me tear my hair out. (I feel like parts weren't made with MVVM in mind, 'cos I can do it fine if I cheat and access my view from the view model.)

Ciaphas fucked around with this message at 00:33 on Aug 27, 2014

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Since we're talking about MVVM and WPF and how the rules work, let me ask a question about breaking said rules. :v:

I don't have my code from work, as usual, but is something like the stuff bordered by slashes a total faux pas? A loose coupling from the view-model to the view? (The specific actual code involves OxyPlot being, unless I really missed something, a bit dumb about trying to automatically pan and zoom to updated data for its plots.)

C# code:
// view code-behind
    public partial class MainWindow : Window
    {
        private MainWindowViewModel MWVM;
        public MainWindow()
        {
            MWVM = new MainWindowViewModel();
            DataContext = MWVM;

            //////////////////////////////////
            MWVM.DataUpdated = OnDataUpdated;
            //////////////////////////////////

            InitializeComponent();
        }
        
        //////////////////////////////
        private void OnDataUpdated()
        {
            MessageBox.Show("fart");
        }
        //////////////////////////////
    }

// view model
    class MainWindowViewModel : ViewModelBase // ViewModelBase implements INotifyParameterChanged and IDisposable if you give a poo poo
    {
        public Action DataUpdated { get; set; }
        
        // blah blah blah

        private void UpdateData() // pretend some RelayCommand bound in the view calls this :effort:
        {
            // blah blah blah

            ///////////////////////////////////
            if (DataUpdated != null)
                DataUpdated();
            ///////////////////////////////////
        }
    }
I know the ideal is that the code-behind of views should be as empty as possible, but I feel my hand is being a bit forced by the way OxyPlot works. I really need a better charting control (cough cough :v:).

Ciaphas fucked around with this message at 06:17 on Aug 27, 2014

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Cool, thanks for the input. I'm still trying to learn WPF and MVVM, so knowing when and why I can/should break the rules is a big help.

Gul Banana posted:

Why not just have the viewmodel set a property RequiresUpdate and have a DataTrigger bound to that?

I forgot about DataTriggers :downs:. I'd still have to have code in the CodeBehind to call MyOxyPlot.ResetAllAxes() or whatever it is, but at least there'd be less plumbing. I'll try that at work today, thanks!

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Am I the only one who thinks chaining dot-notation function calls is a gently caress of a lot easier to read? Or is that my C++ background showing? :v:

C# code:
var q = collectionOfFarts
            .Where(x => x.NumFarts > LOTS_OF_FARTS)
            .Select(x => new { x.StinkLevel, x.NumPeopleWhoNoticed })
            .OrderBy(x => x.StinkLevel)
        ;
that OrderBy after the Select is probably wrong, I always seem to gently caress that up either way

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Assuming the resulting IEnumerable or whatever is the same, the order of your fluent-syntax calls doesn't have an effect on performance, does it? Like doing the .OrderBy() before or after the .Select() or whatever?

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ithaqua posted:

At least with LINQ to Objects, the answer is "of course it makes a difference." I can't speak to the SQL generated by LINQ to Entities. It's usually fairly smart about that kind of thing, but it's safer to assume it's dumb.

Let's say you do this:
code:
var result = fooCollection.OrderBy(x=>x.Id)
.Where(x=>x.Value > 100);
That's going to iterate the entire fooCollection to order it. Then it's going to iterate the entire ordered fooCollection to filter it. You have two full iterations of the initial collection.

If you do
code:
var result = fooCollection.Where(x=>x.Value > 100)
.OrderBy(x=>x.Id);
It's going to filter it first, then order the resulting collection. If the initial collection is large and the filtered collection is small, that could make a giant difference.

That was kind of what I figured, but I wanted to be sure. So much of C# and .NET seems like frank witchcraft to me still :v:

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Trying to relearn WPF and MVVM, and binding is still confusing me a bit. I have an enum called Status in my view model, with possible values of READY and OPENING (and more later). Instead of those, I want to display pretty values in my application's status bar, like "Ready." and "Opening (45%)", for example. Is the way forward to bind a label to the enum normally, write a StatusValueConverter with the strings coded in, and set the label's converter to it? Or is there a better way?

Ideally, that 45% would come from the OpenProgress property in my view model.

(edit) Sorry I can't post any code, separate computers for 'net access here. :(

Ciaphas fucked around with this message at 00:11 on Apr 23, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


GoodCleanFun posted:

Use a property, maybe StatusLabel, that returns a string based on the value of the Status property in a case statement and your OpenProgress property. In the setter for Status, make a call to OnPropertyChanged for StatusLabel.

Bah, that's a lot easier :doh:. Here's me always bloody overthinking it. Thanks lots.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Asynchrony is confusing me a bit, was hoping I could get some help.

My program has a POD class RawRecord. My viewmodel has private List<RawRecord> RawRecords that is populated by a member function private void PopulateRawRecords(), natch, which is called when the viewmodel's FileName property changes. PopulateRawRecords() takes about a minute on the test file I'm using which, as is, blocks the UI. I figured, okay, make it async and call it normally, I don't need to wait on the resulting List<RawRecord> anyway (the UI will just show me the ones that are loaded already along with a progress bar), and this is a good way to learn about async/await. Problem is, PopulateRawRecords() doesn't have any particularly useful points for me to put an await in, so it just runs synchronously anyway.

Question is, what's the idiomatic way to handle this sort of background task when I can't await on any single asynchronous method? Rewrite it so I can?

(Also, if it makes a difference, I'd like to be able to cancel the thing mid-load, but if I understand Task and TokenCancellationSource correctly that doesn't look too hard anyway.)

(Also also, sorry again for no source code. Separate 'net computer again. :()

Ciaphas fucked around with this message at 17:54 on Apr 23, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Bognar posted:

More or less, yes, assuming that the time spent is in IO-heavy work and not CPU heavy work. If it's CPU heavy, then going async isn't going to do anything terribly special for you. You said you're working with a file, so there's an obvious place to use async/await, but I assume you're doing more stuff than just that.

If it's CPU heavy, you can just use await Task.Run(() => PopulateRawRecords()) to get it off the UI thread. Adding a CancellationToken to that is pretty trivial - pass it as a parameter and check if it's cancelled in your processing loop.

Ah, Task.Run looks like what I was looking for; thanks loads for that.

As for whether it's IO or CPU bound, does VS2012 have any useful profiling to work that out or is it kind of guess and check? I can't copy the code here, but here's the overall flow:

C# code:
using (FileStream s = File.OpenRead(FileName))
{
    BinaryReader r = new BinaryReader(s);
    
    Func<bool> findNextRecord = delegate { /* seek to just after sync bytes using r, returning true if found or false for EOF */ }; // necessary due to filler crap
    while (findNextRecord())
    {
        // todo: insert cancellation check here

        UInt16 messageId = r.ReadUInt16();
        byte category = r.ReadByte();
        Int32 param1 = r.ReadInt32BE(); // big endian extension method--yep, they mix LE and BE, just loving shoot me
        // etc etc ad nauseum, probably about 512B worth of crap

        RawRecords.Add(new RawRecord( /* constructor params I can't be assed to write out */));
        RaisePropertyChanged("RawRecords"); // I guess adding to RawRecords doesn't count as changing it, on its own
    }
}
In a typical case, that while loop would run about 300k times.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Inverness posted:

Just at a glance you reading records from a file like that would be IO bound.

A small, irrelevant recommendation. For a using statement you can do something like:
C# code:
using (var r = new BinaryReader(File.OpenRead(FileName))) { ... }
Every well written reader/writer class is will dispose the underlying stream too.
Thanks for this, but in the actual code I need to reference the FileStream object for position and absolute seeking once in a while.

Inverness posted:


Also, no, adding to a collection does not count as changing the value of its property, because the value of the property is still the collection. Whenever you want to listen for collection changes you bind to the collection itself when it implements INotifyCollectionChanged like ObservableCollection<T> does.

I had tried ObservableCollection<RawRecord> yesterday for that very reason, in fact, but I couldn't then figure out how to, say, bind to RawRecords.Count (or is it size? I forget). Thought it'd be something like
XML code:
<Label Name="recordCount" Text="{Binding RawRecords.Count}"/>
<!-- or is it Content, not Text? I forget -->
It seemed to work fine as soon as I made it a List<RawRecord> and called my ObservableBase.RaisePropertyChanged() on it. :shrug:

(edit)

Bognar posted:

Ah, yeah, BinaryReader doesn't expose any async methods so you won't get any benefit out of making your method async. Stick with Task.Run. On a side note, you probably don't need to call RaisePropertyChanged inside your loop, you could just call it once on the outside. Depending on your UI framework, that may or may not have a performance hit.

I call it inside the loop so that the user can view already-loaded records while the rest are loading. If it's a performance problem I can have it called every 100 loops or something, I suppose.

Ciaphas fucked around with this message at 19:30 on Apr 23, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


New question, just to make sure I've got the point. Is there any difference between binding to an ObservableCollection<T>, and binding to a List<T> that has RaisePropertyChanged() called for it on every add/delete?

(Coming from another direction, there's no reason to call RaisePropertyChanged() for an ObservableCollection<T>, right?)

Ciaphas fucked around with this message at 00:14 on Apr 24, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ithaqua posted:

This is a general OO thing. If it's not in a method, it's a field. A field a single instance of an object that is available to all methods within the class.
If it's instantiated in the method, it's local to that method. Once the method finishes, the object goes out of scope and is eligible to be garbage collected.

In this particular case (and not knowing dick about ASP DBContext, mind) I'd guess that a new DBContext implies a new database connection, which you probably don't want for every call.

(At least if it's anything like our Oracle DBs. Connecting takes about five million years, argh argh argh.)

Inverness posted:

If I wanted to bind to count I'd make a property that updated whenever the collection changed.

Yes. They're two fundamentally different things. You need to understand the difference between INotifyPropertyChanged and INotifyCollectionChanged.

:words:

Thanks for all this. I went back to ObservableCollection<RawRecord> today, and strangely the RawRecords.Count bind worked this time. Don't ask me why, I must have typoed hardcore the first time. At any rate this allowed me to take out a bunch of plumbing and cruft, so thanks for that! :)

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Is there a "correct" way to bind a text control's content to non-string data in one's model? My solution was something like this (working from memory, again...):

C# code:
public class RawRecord
{
  // ...
  public byte[] Payload {get; set;}
  public string PayloadHex { get {
    StringBuilder s = new StringBuilder();
    foreach (var b in Payload) s.AppendFormat("{0:X2} ", b); // something like that I think
    return s.ToString();
  }}
}
XML code:
<!-- I might be loving up some property names, going from memory -->
<!-- Also, DataContext is my viewmodel, RawRecords is ObservableCollection<RawRecord> -->
<DataGrid ItemsContent="{Binding RawRecords}">
  <DataGridTextColumn Text="{Binding PayloadHex}" />
  <!-- ... -->
</DataGrid>
It feels vaguely wrong that Just Make Another Property is my answer to all my binding problems when there are properties like ContentsStringFormat on Label for example, but I couldn't work out how to use that with a byte[].

Ciaphas fucked around with this message at 22:43 on Apr 30, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Bognar posted:

This is the classic use case for a Value Converter.

Thanks, I'll give it a proper look later. Seems like a lot more faff than just doing what I did, at least at first glance, though.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ciaphas posted:

Thanks, I'll give it a proper look later. Seems like a lot more faff than just doing what I did, at least at first glance, though.

Quoting myself because now that I've done this, I see the point. Ran into a problem, though. Here's something like my code:
XML code:
<Window x:Class="DataViewer.MainWindow"
...
xmlns:conv="clr-namespace:DataViewer.Converters">
<!-- DataContext set in code behind -->
<DataGrid ItemsSource="{Binding RawRecords}" AutoGenerateColumns="False">
  <DataGridTextColumn Binding="{Binding Bytes, Converter={conv:ByteArrayToHexStringConverter}}" />
</DataGrid>
C# code:
namespace DataViewer.Converters
{
  abstract class ConverterBase : MarkupExtension, IValueConverter
  {
    public override object ProvideValue(IServiceProvicer serviceProvider) { return this; }
    public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
    public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
  }

  class ByteArrayToHexStringConverter : ConverterBase
  {
    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // StringBuilder poo poo }
    public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
  }
}
VS2012 complains about my xaml with the error "The name "ByteArrayToHexStringConverter" does not exist in the namespace "clr-namespace:DataViewer.Converters"." But if I go to build and run, it all works. Is there any way to make that error message go away?


Separate question. My ViewModel has an ObservableCollection<RawRecord> RawRecords. One of RawRecord's properties is Category; in my UI I want to display a distinct list of Categories that are present in the collection. I can easily work out that result into a List<int>--something like RawRecords.Select(x=>x.Category).Distinct().ToList(), I think--but I have no real idea how to bind to that. Is it possible, or am I going to have to create a separate ObservableCollection containing the list of categories I've found?

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Is there a "right" way to open new views in MVVM? In a possibly ill-advised attempt to avoid controlling views from a viewmodel directly, I cobbled together something like this. Is it kind of the right way to go about it?

XML code:
<!-- main window, datacontext is viewmodel -->
<Button Content="Open Sub-window" Command="{Binding OpenSubWindowCommand}"/>
C# code:
interface IWindowService // everything's an object because maybe a test harness view wouldn't use System.Windows.Window???
{
  object OpenWindow(object window);
  bool FocusWindow(object window);
  bool CloseWindow(object window);
}

class WindowService : IWindowService
{
  object OpenWindow(object window)
  {
    // type checks here
    if (window as string == "SubWindow")
    {
      System.Windows.Window w = new Window();
      w.Show();
      return w;
    }
  }
  bool FocusWindow(object window) { return (window as Window).Focus(); }
  bool CloseWindow(object window) { (window as Window).Close(); return true; }
}

class MainWindowViewModel : IDisposable
{
  private IWindowService _MyWindowService; // set in ctor
  public RelayCommand OpenSubWindowCommand = new RelayCommand(x=>OpenSubWindow());

  private object _SubWindow;
  private void OpenSubWindow()
  {
    _SubWindow = _MyWindowService.OpenWindow("SubWindow");
  } 
  
  private void FocusSubWindow() // pretend I had the command etc for this too
  {
    if (_SubWindow != null) _MyWindowService.FocusWindow(_SubWindow);
  }

  public void Dispose()
  {
    if (_SubWindow != null) MyWindowService.CloseWindow(_SubWindow);
  }
}

Ciaphas fucked around with this message at 20:59 on May 7, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:




crashdome posted:

I do something similar. I call mine WpfWindowManager and forego the interface as its explicitly for use only with Wpf views. Although you could try and make an interface to encapsulate different platforms if you want.

Also make it static. I maintain a list of valid views in a servicelocator so the vm calling OpenLogin or whatever in the svclocator. The servicelocator then calls wpfwindowmanager which constructs the appropriate window based on the view registered.

Edit: Oops skipped past Good Cleans post... That is a great example.

Thank you both, I'll give GoodCleanFun's code a lookover and work it out from there. :)

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


With a WPF RelayCommand/DelegateCommand/whatever people are calling it these days, if I have a command that needs to be used across multiple view models, what's the "right" way to share that code out? A separate class inheriting from RelayCommand, then just instantiate & bind to one of those in each viewmodel, or is there some other method? (edit: does the correct way to do things change if the command in question needs to act on the calling viewmodel in some way?)

C# code:
// i probably ballsed up the syntax here somewhere
public class MySharedCommand : RelayCommand
{
    // base const. takes Action<object>, Predicate<object> to implement Execute and CanExecute from ICommand
    public MySharedCommand() : base(Execute, CanExecute) {}
    
    public void Execute(object parms) { ... }
    public bool CanExecute(object parms) { ... }
}
I would have just done this in my project and been done, but something about data-less classes always makes me think I'm Doing It Wrong :(

Ciaphas fucked around with this message at 19:17 on Jul 24, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


While I'm in here, can anyone recommend a book on using MVVM in WPF, or MVVM in general? Going downstairs to the internet machine every five minutes with a new question at work is getting very old, very fast. :(

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


crashdome posted:

Do you work in North Korea?

I get this question and its variants a lot, I was just trying to explain why googling my answers isn't working. I should really just stop mentioning the hosed up situation re: 'net access at work :mad:

Ciaphas fucked around with this message at 22:48 on Jul 24, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


GoodCleanFun posted:

I would just implement the command in a base class that inherits from your ViewModelBase and then have both classes inherit from your new class. That way you retain all your members of ViewModelBase.

:words:

I use the same technique in my ViewModelBase class to create Okay and Close commands that I can easily bind to from a given view. I have a virtual Validate method for Okay which defaults to always execute and I allow Cancel to always execute.

With a DialogCloser attached property, my commands can simply set a bool? DialogResult property which itself is bound to the view's attached property. See this blog: http://blog.excastle.com/2010/07/25/mvvm-and-dialogresult-with-no-code-behind/..

For opening windows, see http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern?fid=1541292&fr=1#xx0xx. I use a super simplified version of this and stick it in my ViewModelBase so I can spawn a new window bound to another viewModel from any given viewModel and grab the result when the window eventually closes from that new viewModel.

Thanks a lot, that makes quite a bit more sense than inheriting from RelayCommand was turning out to. (One of the commands I have to share is, in fact, a Cancel/Close so that's really handy :v:)

Ciaphas fucked around with this message at 22:48 on Jul 24, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


I'm going nuts trying to sort this out in my head. In the MVVM pattern, are ViewModels expected to contain instances of their associated models, or should the actual data be stored/kept somewhere else? If the latter, what the hell should I be googling for?

The first is what I defaulted to trying to learn this, but then I wondered how the hell two viewmodels should be sharing data that they both need to work with. (I sort of alluded to this earlier with the Command question. Really dealing with having multiple viewmodels in general is what's killing me I think :saddowns:)

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


I'm trying to avoid using outside toolkits or libraries, at least at work, because of the Internet thing (I could download it and drag a CD upstairs but :effort:). Is a good inter-VM messaging scheme much of a pain to write?

While I'm at home I'll take a look at MVVM Light, thanks :)

Ciaphas fucked around with this message at 22:51 on Jul 26, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Bognar posted:

The concept isn't complicated, but there can be a lot of weirdness with garbage collection so you have to use WeakReference. Also, if you want your message subscriptions strongly typed (you do), then you have to deal with collections containing multiple generic types. It's annoying enough that you're probably better off picking up someone else's battle tested implementation.

Also who restricts a programmer's internet? That's just a guaranteed destruction of productivity.

Sounds like :effort: is in favor of getting MVVMLight or some other library, then. Thanks. :)

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Coworker and I are having a minor disagreement on where to implement a few windows events on a ListView--specifically, DragOver, Drop, and KeyPressed (delete key, specifically, for removing entries). Right now he has them implemented as markup extension commands, instead of normal code-behind events; this means they (and their business logic) can go straight to the viewmodel. I feel that, because they use things like the DragOverEventArgs as the CommandParameter, which are inherently UI/view related, they have no business going to the VM at all--at least, not before parsing those event args into a list of files that were dropped, in this case--and should have been normal code behind events, despite the usual thing about code-behind being bad or whatever.

What do you think? Either way works, we're both just still learning the 'right' way to do things in this cockamamie MVVM thing.

(Sorry I can't post any code. :()

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Ithaqua posted:

Here's my guideline: Does the behavior you're implementing interact with the underlying data in any way? For example, if the "drop" interaction results in data moving from one observable collection to another, then that's not purely view related. It's the view manipulating the viewmodel via UI interactions, same as typing text into a textbox updates the viewmodel.

The UI behavior itself can be in the view, that's fine. But the interaction with the data shouldn't happen in the view. Of course, it's a lot less important if you know 100% for a fact that you will never, ever hook a different view up to that viewmodel. No need to be dogmatic about things. Patterns and practices are great for giving you a general direction, but there are going to be real world cases where doing things THE RIGHT WAY is not worth the effort.

Like with unit testing: A few years ago, I was 100% "everything should be unit tested all the time no exceptions". Now I've backed off a bit to "everything critical or complex should be unit tested".

For production, no, the view and viewmodel in question are proverbially joined at the hip. My only thinking was that, if I dig the Windows-y UI poo poo (DragEventArgs in this case) out of the viewmodel and leave it in the view's code-behind, we can attach a different view later--say, a console for headless unit tests. (And this viewmodel in question is one that does very much need to be tested.)

Plus, neither of us really know what we're doing when it comes to MVVM yet; going against dogma when we don't quite know what the dogma is is kinda hard. What's the old saw, the rules are there to make you at least think when you're breaking them? Yeah, I don't know the rules yet, hence these posts :v:

The specific use case here is that drag-dropping a file should attempt to open it, adding it to the VM's ObservableCollection<Request> and thus displaying the dropped file in the ListView it was dropped on via binding. This was already implemented as a command leading to a method in the VM via a plain old button. So my figuring was, I'd move the actual drop event to the code behind, have it extract the file name, then call the VM's Open method directly (via something like (DataContext as MyViewModel).Open(fileName)).

Ciaphas fucked around with this message at 16:18 on Jul 30, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Thanks to both of you. My coworker and I will discuss what you said when we meet again today. :)

Separate question entirely I just remembered, and one a hell of a lot more general/easier to answer (I hope). Is this sufficient for overriding Equals(), or am I missing a piece? (I may have ballsed up the syntax--again, different machine)

C# code:
class Request : IEquatable<Request>
{
   public string FileName {get; set;} // whatever

   public override bool Equals(object rhs)
   {
      if (base.Equals(rhs)) return true;
      if (rhs.Type() != this.Type()) return false;

      return Equals(rhs as Request);
   }
   public bool Equals(Request rhs) // implements IEquatable
   {
      return FileName == rhs.FileName; // or whatever other value semantics...
   }
}

Ciaphas fucked around with this message at 16:25 on Jul 30, 2015

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Sedro posted:

You need to implement GetHashCode, and yours will throw exceptions when comparing to null.

You shouldn't be writing this boilerplate. Here's what R# generates:
[code elided]
You can also use compile-time instrumentation (e.g. Fody) which is a bit more maintainable than code generation

Sorry, what's R#? Google's coming up with some statistical programming language, which given context I don't think is right :v:

Adbot
ADBOT LOVES YOU

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


ReSharper, I vaguely remember the dev team I'm helping was working on getting that, I think. I'll have to look at what it is, I've never heard of it, but if it helps me deal with bullshit boilerplate like that Equals stuff (thanks for that by the way) I'm all for it :v:

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply