Pages

Tuesday, July 21, 2020

Unforgivable Sins

Last night we had a power outage, so I left my PC off all night.  When I rebooted, guess what happened?

Slack - a program I had very clearly configured to run-on-demand suddenly decided it needed to launch on startup.  I don't now and never have used Slack as a primary means of communication, and the thought that they would take ownership of my PC because they think they know what's good for me is - well it's unforgivable.

Uninstalled.

When you write code, I know it's easier to think your customers, pirates, and family members love your software so much that they want it front and center 100% of the time, but mostly, they don't.  In fact, my computer boots so slowly that I purposely turn off all those autoupdaters that run on startup.

Google Updater, Adobe Updater, Java Updater, Daz, Unity, Apple (for iTunes, another program I rarely use), Avast, Malware Bytes, Carbonite.  All of these updaters are disabled, because when I reboot, it takes my hard drive 15 minutes to fully boot.  The mouse and icons are there early, but if I try to launch anything it takes 300x as long and it's just not worth it.

Every time I have to disable these malware bots, I have to search startup, the task scheduler, and 4 other places to make sure they haven't found some new, hidden way to infiltrate my system.  It wouldn't be so bad if there was a task in the scheduler that would launch the updater, say at 3:46AM and check for a patch, then CLOSE THE UPDATER, but no, the updater must remain running for the rest of time.  Some software companies even have a second program that makes sure the first program is never stopped.

Programmers: These practices will get you into the Special Hell.  The one reserved for child molesters and people who talk in the theater.

...


Bryan Valencia is a contributing editor and founder of Visual Studio Journey.  He owns and operates Software Services, a web design and hosting company in Manteca, California.

Thursday, July 16, 2020

The solution you are opening is bound to source control on the following Azure DevOps Server:

Unlike Git or Subversion, when you move your projects to a new path, say to organize your hard drive, you'll see this warning when you open your project.

---------------------------
Microsoft Visual Studio
---------------------------
Team Foundation Server Version Control

The solution you are opening is bound to source control on the following Azure DevOps Server: http://...collection. Would you like to contact this server to try to enable source control integration?
---------------------------
Yes   No   Help  
---------------------------

Pressing Yes is useless.  Right clicking your project gives you the option of adding it, not reconnecting it.

Here's the procedure to fix this issue.

  1. Close your solution.
  2. Close the What would you like to do? window.
  3. File -> Source Control -> Advanced -> Workspaces.
  4. You may see many workspaces in the list, highlight the one that matters.
  5. Edit.
  6. You will see a list of Source Control Folders connected to Local folders. 
  7. Click on the Local Folder (you'll see the OLD location) and fix it to the new location.
  8. Repeat for all the solutions you want to fix.
  9. OK, Close
  10. Bob's your uncle.  You can edit the project without the scary warning with the "fix" that doesn't work.
  11. (optional, but recommended) Ditch Azure Devops Because it's lame, and use Subversion or Git.

Friday, July 3, 2020

ASP.NET MVC - Simple 5 Step JQuery Filter.

Using this 5 step process you can add a client-side JQuery filter to your MVC Index pages.   

  1. Set up your layout page so that the JQuery pages load BEFORE the page loads.
  2. Add a filter bar.
  3. Add a <thead> and <tbody> section to your table (so we don't inadvertently filter the header row).
  4. Add data- tags to your <tr>s.
  5. Add jQuery to respond to the keyup event in your filters.

Layout page

In your Views/Shared/_Layout page, there is a block of code at the end that looks like this:

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)

Move these to the line just after

    @Scripts.Render("~/bundles/modernizr")

...at around line 8.  The JQuery library needs to be loaded before the @RenderBody line.  This step only needs to happen once for your whole app, unless you're using multiple layout pages.

Filter Bar

Next we'll need a filter bar to the index page.

Let's say we want to filter by three fields:

I put this just before the <table>.

<div class="icon-bar">
    <span style="font-weight:bold;">Filters</span>&nbsp;&nbsp;
    State: <input name="State" id="state" type="text" style="width:35px;" maxlength="2" class="filter" />&nbsp;&nbsp;
    County: <input name="County" id="county" type="text" style="width:90px;" class="filter" />&nbsp;&nbsp;
    Town: <input name="Town" id="town" type="text" style="width:90px;" class="filter" />&nbsp;&nbsp;
    <span id="recordCount" style="font-weight:bold;">@Model.Count().ToString() records.</span>
</div>

class="icon-bar": use this class to style the bar how you like it: I used this in my style sheet, but you do as you like:

div.icon-bar{
    background:linear-gradient(#44f, #88F, #44F, #008, #000);
    padding:5px;
    margin-bottom:12px;
    border:1px solid black;
    border-radius:15px;
    color:white;
}

This makes the text in the input boxes white on white, so I added this:

input.filter{
    background-color:white;
    color:black;
}

thead and tbody

The first row in your table is the headers and we DON'T want to ever filter that row out.  So add a <thead> tag around the first <tr>

Now add a <tbody id="fbody"> around your data loop.

I use the id tag to find the tbody and access its rows in the jQuery.

Data- tags

Rather than use some wizardry to find what's in the <td> cells, I add custom tags to the <tr>s to help me hide/show them as a group.

@foreach (var item in Model){<tr data-state="@item.state" data-county="@item.county" data-town="@item.town">    

This adds tags for all the fields I want to filter for.

jQuery

This is easy enough.  Add a keyup function to all 3 filter fields.

<script type="text/javascript">
    $(".filter").keyup(function () { //this matches the class=filter on all 3 input boxes.
        //hide all trs in the tbody
        var rows = $("#fbody").find("tr").hide(); //this hides every row in the table body
        var workingList = rows;  //makes a list of all the table rows to work on
        //grab the text from the search box
        var state = $("#state").val().toLowerCase();
        var county = $("#county").val().toLowerCase();
        var town = $("#town").val().toLowerCase();

        //now show the ones where all values match

        if (state.length > 0) { //if there is any text in the first filter...
            //filter just returns a filtered list
            workingList = workingList.filter(function () {  //spawns a tiny inline function to test each row
                //return true if data-state starts with var state
                if ($(this).data("state").toLowerCase().indexOf(state) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }

        if (county.length > 0) {
            workingList = workingList.filter(function () {
                //return true if data-state starts with var state
                if ($(this).data("county").toLowerCase().indexOf(county) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }

        if (town.length > 0) {
            workingList = workingList.filter(function () {
                //return true if data-state starts with var state
                if ($(this).data("town").toLowerCase().indexOf(town) == 0) {
                    return true;
                }
                else {
                    return false;
                }
            })
        }
        $(workingList).show();  //show the rows remaining in the working list.
        $("#recordCount").text(workingList.length+" records.") //update the record count in the filter bar.
    });
</script>


That's it! You can see how I
  1. hide all the rows in the <tbody>
  2. grab the strings from the filter boxes.
  3. copy the list of all rows to workingList.
  4. Loop through the filters...
    1. check to see if there is any text in the filter strings.  (if not, skip that filter.)
    2. enact a function on each <tr> that looks for the data- values to match the string entered by the user.
    3. this filters out all the <tr>s that do not match.
    4. the 3 filters all work together successively eliminating more rows from workingList until we are done.
  5. we issue a show() to all the rows that are left in the list.
  6. we update the count in the filter bar.

Also, you C# programmers like me: Don't freak out that there are returns in the middle of the function.  Those returns are in the middle of a small inline function, and return to the main function, not from it.

Matching

I chose a "startsWith" match, that's why my functions have a .indexof(state)==0.  Here's some different ways to match.

exact match

if ($(this).data("town").toLowerCase() == town) {

contains match

if ($(this).data("town").toLowerCase().indexOf(town) >= 0) {      This should be a good start to filtering for any ASP.NET MVC index page using jQuery.                

Share This!

Contact Us

Name

Email *

Message *